]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/client/sbar.qc
CSQC part for the new score system (not really finished yet, needs cleaning)
[divverent/nexuiz.git] / data / qcsrc / client / sbar.qc
1
2 float last_weapon;
3 float activeweapon;
4 float weapontime;
5
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
7
8 vector sbar;
9 vector sbar_fontsize;
10 float sbar_alpha_fg;
11 float sbar_hudselector;
12 /*
13 entity sortedPlayers;
14 entity sortedTeams;
15
16 .float sb_frags;
17 .float sb_team;
18 .float sb_player;
19 .float sb_caps;
20 */
21 float ps_primary, ps_secondary;
22 float ts_primary, ts_secondary;
23
24 entity team1, team2, team3, team4, teamspec;
25
26 void CSQC_kh_hud();
27 void CSQC_ctf_hud();
28 void MapVote_Draw();
29 void Sbar_FinaleOverlay()
30 {
31         /*vector pos;
32         pos_x = (vid_conwidth - 1)/2;
33         pos_y = 16;
34         pos_z = 0;*/
35         
36         //drawpic(pos, "gfx/finale", '0 0', '1 1 1', sbar_alpha_fg, DRAWFLAG_NORMAL);
37         
38         //drawstring(pos, "END", sbar_fontsize, '1 1 1', 1, DRAWFLAG_NORMAL);
39         MapVote_Draw();
40 }
41
42 void Sbar_DrawWeapon(float nr, float fade, float active)
43 {
44         vector pos, vsize, color;
45         float value;
46         
47         value = (active) ? 1 : 0.6;
48         color_x = color_y = color_z = value;
49         
50         if(sbar_hudselector == 1)
51         {
52                 // width = 300, height = 100
53                 const float w_width = 32, w_height = 12, w_space = 2, font_size = 8;
54                 
55                 pos_x = (vid_conwidth - w_width * 9) * 0.5 + w_width * nr;
56                 pos_y = (vid_conheight - w_height);
57                 pos_z = 0;
58                 vsize_x = w_width;
59                 vsize_y = w_height;
60                 vsize_z = 0;
61                 drawpic(pos, strcat("gfx/inv_weapon", ftos(nr)), vsize, color, value * fade * sbar_alpha_fg, 0);
62                 pos_x += w_space;
63                 pos_y += w_space;
64                 vsize_x = font_size;
65                 vsize_y = font_size;
66                 vsize_z = 0;
67                 drawstring(pos, ftos(nr+1), vsize, '1 1 0', sbar_alpha_fg, 0);
68
69         }
70         else
71         {
72                 // width = 300, height = 100
73                 const float w2_width = 300, w2_height = 100, w2_space = 10;
74                 const float w2_scale = 0.4;
75
76                 pos_x = vid_conwidth - (w2_width + w2_space) * w2_scale;
77                 pos_y = (w2_height + w2_space) * w2_scale * nr + w2_space;
78                 pos_z = 0;
79                 vsize_x = w2_width * w2_scale;
80                 vsize_y = w2_height * w2_scale;
81                 vsize_z = 0;
82                 
83                 drawpic(pos, strcat("gfx/inv_weapon", ftos(nr)), vsize, color, value * fade * sbar_alpha_fg, 0);
84         }
85 }
86 void Sbar_DrawXNum (vector pos, float num, float digits, float lettersize, vector rgb, float a, float dflags)
87 {
88         float l, i;
89         string str, tmp;
90         float minus;
91         vector vsize;
92
93         vsize_x = vsize_y = lettersize;
94         vsize_z = 0;
95
96         if(num < 0)
97         {
98                 minus = true;
99                 num = -num;
100                 pos_x -= lettersize;
101         } else
102                 minus = false;
103         
104         if(digits < 0)
105         {
106                 tmp = ftos(num);
107                 digits = -digits;
108                 str = strcat(substring("0000000000", 0, digits - strlen(tmp)), tmp);
109         } else
110                 str = ftos(num);
111         
112         l = strlen(str);
113
114         if(l > digits)
115         {
116                 str = substring(str, l-digits, 999);
117                 l = strlen(str);
118         } else if(l < digits)
119                 pos_x += (digits-l) * lettersize;
120
121         if(minus)
122         {
123                 drawpic(sbar + pos, "gfx/num_minus", vsize, rgb, a * sbar_alpha_fg, dflags);
124                 pos_x += lettersize;
125         }
126
127         for(i = 0; i < l; ++i)
128         {
129                 drawpic(sbar + pos, strcat("gfx/num_", substring(str, i, 1)), vsize, rgb, a * sbar_alpha_fg, dflags);
130                 pos_x += lettersize;
131         }
132 }
133
134 void Cmd_Sbar_SetFields(float argc);
135 void Sbar_InitScores()
136 {
137         float i, f, primary_prio, secondary_prio;
138
139         primary_prio = secondary_prio = -1;
140         for(i = 0; i < MAX_SCORE; ++i)
141         {
142                 f = (scores_flags[i] & SFL_SORT_PRIO_MASK);
143                 if(f > primary_prio) {
144                         ps_secondary = ps_primary;
145                         ps_primary = i;
146                 } else if(f > secondary_prio)
147                         ps_secondary = i;
148         }
149         
150         primary_prio = secondary_prio = -1;
151         for(i = 0; i < MAX_TEAMSCORE; ++i)
152         {
153                 f = (teamscores_flags[i] & SFL_SORT_PRIO_MASK);
154                 if(f > primary_prio) {
155                         ts_secondary = ts_primary;
156                         ts_primary = i;
157                 } else if(f > secondary_prio)
158                         ts_secondary = i;
159         }
160
161         Cmd_Sbar_SetFields(0);
162 }
163
164 void Sbar_UpdatePlayerPos(entity pl);
165 void Sbar_UpdatePlayerTeams()
166 {
167         float Team;
168         entity pl, tmp;
169         float num;
170
171         num = 0;
172         for(pl = players.sort_next; pl; pl = pl.sort_next)
173         {
174                 num += 1;
175                 Team = GetPlayerColor(pl.sv_entnum);
176                 if(pl.team != Team)
177                 {
178                         tmp = GetTeam(pl.team, false);
179                         tmp.team_size -= 1;
180                         tmp = GetTeam(Team, true);
181                         tmp.team_size += 1;
182                         
183                         pl.team = Team;
184
185                         tmp = pl.sort_prev;
186                         Sbar_UpdatePlayerPos(pl);
187                         if(tmp)
188                                 pl = tmp;
189                         else
190                                 pl = players.sort_next;
191                 }
192         }
193         //print(strcat("PNUM: ", ftos(num), "\n"));
194 }
195
196 float Sbar_ComparePlayerScores(entity left, entity right)
197 {
198         float vl, vr;
199         vl = GetPlayerColor(left.sv_entnum);
200         vr = GetPlayerColor(right.sv_entnum);
201         
202         if(vl > vr)
203                 return true;
204         if(vl < vr)
205                 return false;
206
207         vl = left.scores[ps_primary];
208         vr = right.scores[ps_primary];
209         if(vl > vr)
210                 return IS_INCREASING(scores_flags[ps_primary]);
211         if(vl < vr)
212                 return IS_DECREASING(scores_flags[ps_primary]);
213         
214         vl = left.scores[ps_secondary];
215         vr = right.scores[ps_secondary];
216         if(vl > vr)
217                 return IS_INCREASING(scores_flags[ps_secondary]);
218         if(vl < vr)
219                 return IS_DECREASING(scores_flags[ps_secondary]);
220         
221         return false;
222 }
223
224 void Sbar_UpdatePlayerPos(entity player)
225 {
226         for(other = player.sort_next; other && Sbar_ComparePlayerScores(player, other); other = player.sort_next)
227         {
228                 SORT_SWAP(player, other);
229         }
230         for(other = player.sort_prev; other != players && Sbar_ComparePlayerScores(other, player); other = player.sort_prev)
231         {
232                 SORT_SWAP(other, player);
233         }
234 }
235
236 float Sbar_CompareTeamScores(entity left, entity right)
237 {
238         float vl, vr;
239         
240         vl = left.teamscores[ts_primary];
241         vr = right.teamscores[ts_primary];
242         if(vl > vr)
243                 return IS_INCREASING(teamscores_flags[ts_primary]);
244         if(vl < vr)
245                 return IS_DECREASING(teamscores_flags[ts_primary]);
246         
247         vl = left.teamscores[ts_secondary];
248         vr = right.teamscores[ts_secondary];
249         if(vl > vr)
250                 return IS_INCREASING(teamscores_flags[ts_secondary]);
251         if(vl < vr)
252                 return IS_DECREASING(teamscores_flags[ts_secondary]);
253
254         return false;
255 }
256
257 void Sbar_UpdateTeamPos(entity Team)
258 {
259         for(other = Team.sort_next; other && Sbar_ComparePlayerScores(Team, other); other = Team.sort_next)
260         {
261                 if(other.team == COLOR_SPECTATOR)
262                         break;
263                 SORT_SWAP(Team, other);
264         }
265         for(other = Team.sort_prev; other != teams && Sbar_ComparePlayerScores(other, Team); other = Team.sort_prev)
266         {
267                 SORT_SWAP(other, Team);
268         }
269 }
270
271 void Cmd_Sbar_Help(float argc)
272 {
273         print("You can modify the scoreboard using the\n");
274         print("^3|---------------------------------------------------------------|\n");
275         print("^1 TO BE DONE\n");
276         print("Usage:\n");
277         print("^2sbar_columns_set ^7filed1 field2 ...\n");
278         print("The following field names are recognized (case INsensitive):\n");
279         print("You can use a ^3|^7 to start the right-aligned fields.\n");
280         
281         print("^3name^7 or ^3nick^7             Name of a player\n");
282         print("^3ping^7                     Ping time\n\n");
283         print("^3kd^7 or ^3kdr^7 or ^3kdratio^7 or ^3k/d\n");
284         print("                         The kill-death ratio\n");
285
286 /*
287         print("^3caps^7 or ^3captures^7         Number of flags captured\n");
288         print("^3rets^7 or ^3returns^7          Number of flags returned\n");
289         print("^3frags^7 or ^3kills^7           Frags\n");
290         print("^3deaths^7 or ^3dths^7           Number of deaths\n");
291         */
292         local float i;
293         print("Additional columns:\n");
294         for(i = 0; i < MAX_SCORE; ++i)
295         {
296                 if(scores_label[i])
297                         print(strcat(scores_label[i], "\n"));
298         }
299 }
300
301 #define MIN_NAMELEN 24
302 #define MAX_NAMELEN 24
303
304 string Sbar_DefaultColumnLayout()
305 {
306         switch(gametype)
307         {
308                 case GAME_CTF: return "ping name | caps frags";
309                 case GAME_KEYHUNT: return "ping name | score caps kills";
310                 default: return "ping name | frags";
311                         // TODO: add other gametypes
312         }
313 }
314
315 void Cmd_Sbar_SetFields(float argc)
316 {
317         float i, j;
318         string str;
319         float digit;
320
321         // TODO: re enable with gametype dependant cvars?
322         if(argc < 2) // no arguments provided
323                 argc = tokenize(strcat("x ", cvar_string("sbar_columns")));
324
325         if(argc < 2)
326                 argc = tokenize(strcat("x ", Sbar_DefaultColumnLayout()));
327         
328         argc = min(MAX_SBAR_FIELDS, argc);
329         sbar_num_fields = 0;
330
331         drawfont = sbar_font;
332         digit = stringwidth("0123456789", FALSE) / 10;
333
334         argc = min(argc-1, MAX_SBAR_FIELDS-1);
335         for(i = 0; i < argc; ++i)
336         {
337                 str = argv(i+1);
338                 strunzone(sbar_title[i]);
339                 sbar_title[i] = strzone(str);
340                 sbar_size[i] = stringwidth(str, FALSE);
341                 str = strtolower(str);
342
343                 
344
345                 if(str == "ping") {
346                         sbar_field[i] = SP_PING;
347                 } else if(str == "name" || str == "nick") {
348                         sbar_field[i] = SP_NAME;
349                         sbar_size[i] = MIN_NAMELEN; // minimum size? any use?
350                 } else if(str == "|") {
351                         sbar_field[i] = SP_SEPARATOR;
352                 } else {
353                         for(j = 0; j < MAX_SCORE; ++j)
354                         {
355                                 if(str == strtolower(scores_label[j]))
356                                         break;
357                         }
358                         if(j == MAX_SCORE) {
359                                 print(strcat("^1Error:^7 Unknown score field: '", str, "'\n"));
360                                 --sbar_num_fields;
361                         } else
362                                 sbar_field[i] = j;
363                 }
364                 ++sbar_num_fields;
365         }
366         sbar_field[i] = SP_END;
367 }
368
369 // MOVEUP::
370 vector sbar_field_rgb;
371 string Sbar_GetField(entity pl, float field)
372 {
373         float tmp, num, denom;
374         string str;
375         sbar_field_rgb = '1 1 1';
376         switch(field)
377         {
378                 case SP_PING:
379                         str = bufstr_get(databuf, DATABUF_PING + pl.sv_entnum);
380                         tmp = max(0, min(220, stof(str)-80)) / 220;
381                         sbar_field_rgb = '1 1 1' - '0 1 1'*tmp;
382                         return str;
383                 
384                 case SP_NAME:
385                         return getplayerkey(pl.sv_entnum, "name");
386
387                 case SP_KDRATIO:
388                         num = pl.(scores[SP_KILLS]);
389                         denom = pl.(scores[SP_DEATHS]);
390
391                         if(denom == 0) {
392                                 sbar_field_rgb = '0 1 0';
393                                 str = ftos(num);
394                         } else if(num <= 0) {
395                                 sbar_field_rgb = '1 0 0';
396                                 str = ftos(num/denom);
397                         } else
398                                 str = ftos(num/denom);
399                 
400                         tmp = strstrofs(str, ".", 0);
401                         if(tmp > 0)
402                                 str = substring(str, 0, tmp+2);
403                         return str;
404                         
405                 default:
406                         return ftos(pl.(scores[field]));
407         }
408         return "error";
409 }
410
411 // shamelessly stolen from menu QC :P <- as if I would steal YOUR code pfft ;)
412 float textLengthUpToWidth(string theText, float maxWidth, float handleColors)
413 {
414         // STOP.
415         // The following function is SLOW.
416         // For your safety and for the protection of those around you...
417         // DO NOT CALL THIS AT HOME.
418         // No really, don't.
419         if(stringwidth(theText, handleColors) <= maxWidth)
420                 return strlen(theText); // yeah!
421
422         // binary search for right place to cut string
423         float left, right, middle; // this always works
424         left = 0;
425         right = strlen(theText); // this always fails
426         do
427         {
428                 middle = floor((left + right) / 2);
429                 if(stringwidth(substring(theText, 0, middle), handleColors) <= maxWidth)
430                         left = middle;
431                 else
432                         right = middle;
433         }
434         while(left < right - 1);
435
436         // NOTE: when color codes are involved, this binary search is,
437         // mathematically, BROKEN. However, it is obviously guaranteed to
438         // terminate, as the range still halves each time - but nevertheless, it is
439         // guaranteed that it finds ONE valid cutoff place (where "left" is in
440         // range, and "right" is outside).
441
442         return left;
443 }
444 string textShortenToWidth(string theText, float maxWidth, float handleColors)
445 {
446         if(stringwidth(theText, handleColors) <= maxWidth)
447                 return theText;
448         else
449                 return strcat(substring(theText, 0, textLengthUpToWidth(theText, maxWidth - stringwidth("...", handleColors), handleColors)), "...");
450 }
451
452 float xmin, xmax, ymin, ymax, sbwidth, sbheight;
453
454 void Sbar_PrintScoreboardItem(vector pos, entity pl, float is_self)
455 {
456         vector tmp;
457         string str;
458         float i, field, len;
459         float is_spec;
460         is_spec = (GetPlayerColor(pl.sv_entnum) == COLOR_SPECTATOR);
461
462         // Layout:
463         tmp_z = 0;
464         if(is_self)
465         {
466                 tmp_x = sbwidth;
467                 tmp_y = sbar_fontsize_y;
468                 drawfill(pos - '1 1', tmp + '2 2', '1 1 1', 0.3, DRAWFLAG_NORMAL);
469         }       
470         tmp_y = 0;
471         
472         for(i = 0; i < sbar_num_fields; ++i)
473         {
474                 field = sbar_field[i];
475                 if(field == SP_SEPARATOR)
476                         break;
477
478                 if(is_spec && field != SP_NAME && field != SP_PING) {
479                         pos_x += sbar_fontsize_x*sbar_size[i] + sbar_fontsize_x;
480                         continue;
481                 }
482                 str = Sbar_GetField(pl, field);
483
484                 if(field == SP_NAME)
485                 {
486                         float realsize;
487                         float j;
488                         realsize = sbar_size[i];
489                         if(i+1 < sbar_num_fields)
490                                 if(sbar_field[i+1] == SP_SEPARATOR)
491                                 {
492                                         realsize = (xmax - xmin) / sbar_fontsize_x;
493                                         for(j = 0; j < sbar_num_fields; ++j) if(j != i) if(sbar_field[j] != SP_SEPARATOR)
494                                                 realsize -= sbar_size[j] + 1;
495                                         realsize += 1;
496                                 }
497                         str = textShortenToWidth(str, realsize, TRUE);
498                 }
499                 len = stringwidth(str, TRUE);
500                 
501                 if(sbar_size[i] < len)
502                         sbar_size[i] = len;
503
504                 pos_x += sbar_fontsize_x*sbar_size[i] + sbar_fontsize_x;
505
506                 if(field == SP_NAME) {
507                         tmp_x = sbar_fontsize_x*sbar_size[i] + sbar_fontsize_x;
508                         drawcolorcodedstring(pos - tmp, str, sbar_fontsize, 1, DRAWFLAG_NORMAL);
509                 } else {
510                         tmp_x = len*sbar_fontsize_x + sbar_fontsize_x;
511                         drawstring(pos - tmp, str, sbar_fontsize, sbar_field_rgb, 1, DRAWFLAG_NORMAL);
512                 }
513         }
514         
515         if(sbar_field[i] == SP_SEPARATOR)
516         {
517                 pos_x = xmax;
518                 for(i = sbar_num_fields-1; i > 0; --i)
519                 {
520                         field = sbar_field[i];
521                         if(field == SP_SEPARATOR)
522                                 break;
523                         
524                         if(is_spec && field != SP_NAME && field != SP_PING) {
525                                 pos_x -= sbar_fontsize_x*sbar_size[i] + sbar_fontsize_x;
526                                 continue;
527                         }
528                         
529                         str = Sbar_GetField(pl, field);
530
531                         if(field == SP_NAME)
532                                 str = textShortenToWidth(str, sbar_size[i], TRUE);
533                         len = stringwidth(str, TRUE);
534
535                         if(sbar_size[i] < len)
536                                 sbar_size[i] = len;
537
538                         if(field == SP_NAME) {
539                                 tmp_x = sbar_fontsize_x*len; // left or right aligned? let's put it right...
540                                 drawcolorcodedstring(pos - tmp, str, sbar_fontsize, 1, DRAWFLAG_NORMAL);
541                         } else {
542                                 tmp_x = sbar_fontsize_x*len; //strlen(str);
543                                 drawstring(pos - tmp, str, sbar_fontsize, sbar_field_rgb, 1, DRAWFLAG_NORMAL);
544                         }
545                         pos_x -= sbar_fontsize_x*sbar_size[i] + sbar_fontsize_x;
546                 }
547         }
548 }
549
550 void Sbar_DrawScoreboard()
551 {
552         //float xmin, ymin, xmax, ymax;
553         vector rgb, pos, tmp, sbar_save;
554         entity pl, tm;
555         float specs, i;
556         float center_x;
557
558         sbar_fontsize = Sbar_GetFontsize();
559         if(sbar_fontsize_x == 0)
560                 sbar_fontsize = '8 8 0';
561         if(sbar_fontsize_y == 0)
562                 sbar_fontsize_y = sbar_fontsize_x;
563         
564         xmin = vid_conwidth / 5;
565         ymin = 20;
566
567         xmax = vid_conwidth - xmin;
568         ymax = vid_conheight - 0.2*vid_conheight;
569
570         sbwidth = xmax - xmin;
571         sbheight = ymax - ymin;
572
573         center_x = xmin + 0.5*sbwidth;
574
575         //Sbar_UpdateFields();
576
577         // Initializes position
578         //pos_x = xmin;
579         pos_y = ymin;
580         pos_z = 0;
581
582         // Heading
583         drawfont = sbar_font;
584         pos_x = center_x - stringwidth("Scoreboard", TRUE)*0.5*24;
585         drawstring(pos, "Scoreboard", '24 24', '1 1 1', 1, DRAWFLAG_NORMAL);
586         pos_x = xmin;
587         pos_y += 24 + 4;
588
589         // Titlebar background:
590         tmp_x = sbwidth;
591         tmp_y = sbar_fontsize_y;
592         drawfill(pos - '1 1', tmp + '2 2', '0.5 0.5 0.5', 0.5, DRAWFLAG_NORMAL);
593         
594         for(i = 0; i < sbar_num_fields; ++i)
595         {
596                 if(sbar_field[i] == SP_SEPARATOR)
597                         break;
598                 drawstring(pos, sbar_title[i], sbar_fontsize, '1 1 1', 1, DRAWFLAG_NORMAL);
599                 pos_x += sbar_fontsize_x*sbar_size[i] + sbar_fontsize_x;
600         }
601         
602         if(sbar_field[i] == SP_SEPARATOR)
603         {
604                 pos_x = xmax + sbar_fontsize_x;
605                 tmp_y = tmp_z = 0;
606                 for(i = sbar_num_fields-1; i > 0; --i)
607                 {
608                         if(sbar_field[i] == SP_SEPARATOR)
609                                 break;
610                         
611                         pos_x -= sbar_fontsize_x*sbar_size[i] + sbar_fontsize_x;
612                         /**
613                          * FTEQCC BUG!
614                          * Using the following line will fuck it all up:
615                          **
616                          * tmp_x = sbar_size[i] - strlen(sbar_title[i])*8;
617                          */
618                         tmp_x = sbar_fontsize_x*sbar_size[i];
619                         tmp_x -= stringwidth(sbar_title[i], FALSE)*sbar_fontsize_x;
620                         drawstring(pos + tmp, sbar_title[i], sbar_fontsize, '1 1 1', 1, DRAWFLAG_NORMAL);
621                 }
622         }
623                 
624         pos_x = xmin;
625         pos_y += 1.5 * sbar_fontsize_y;
626
627         sbar_save = sbar;
628         sbar = '0 0 0';
629         
630         if(teamplay)
631         {
632                 //for(tm = sortedTeams.sort_next; tm; tm = tm.sort_next)
633                 for(tm = teams.sort_next; tm; tm = tm.sort_next)
634                 {
635                         if(!tm.team_size || tm.team == COLOR_SPECTATOR)
636                                 continue;
637
638                         rgb = GetTeamRGB(tm.team);
639
640                         pos_x = xmin - 4*24;
641                         // TODO: Print primary and secondary scores!
642                         
643                         pos_x = xmin;
644
645                         specs = tm.team_size;
646
647                         if(specs < 2)
648                                 specs = 2;
649                         
650                         tmp_x = sbwidth;
651                         tmp_y = 1.25 * sbar_fontsize_y * specs;
652                         drawfill(pos - '1 1', tmp + '2 0', rgb, 0.2, DRAWFLAG_NORMAL);
653                         
654                         for(pl = players.sort_next; pl; pl = pl.sort_next)
655                         {
656                                 if(pl.team != tm.team)
657                                         continue;
658                                 Sbar_PrintScoreboardItem(pos, pl, (pl.sv_entnum == player_localentnum - 1));
659                                 pos_y += 1.25 * sbar_fontsize_y;
660                                 tmp_y -= 1.25 * sbar_fontsize_y;
661                         }
662                         pos_y += tmp_y + 1.5 * sbar_fontsize_y;
663                 }
664                 // rgb := tempvector :)
665                 rgb = pos + '0 1.5 0' * sbar_fontsize_y;
666                 pos_y += 3 * sbar_fontsize_y;
667                 specs = 0;
668                 for(pl = players.sort_next; pl; pl = pl.sort_next)
669                 {
670                         if(pl.team != COLOR_SPECTATOR)
671                                 continue;
672                         //drawcolorcodedstring(pos, getplayerkey(pl.sb_player, "name"), '8 8 0', 1, 0);
673                         Sbar_PrintScoreboardItem(pos, pl, (pl.sv_entnum == player_localentnum - 1));
674                         pos += '0 1.25 0' * sbar_fontsize_y;
675                         ++specs;
676                 }
677                         
678                 if(specs)
679                         drawstring(rgb, "Spectators", sbar_fontsize, '1 1 1', 1, 0);
680         } else {
681                 pos_x = xmin;
682                 for(pl = players.sort_next; pl; pl = pl.sort_next)
683                 {
684                         if(pl.team == COLOR_SPECTATOR)
685                                 continue;
686                         Sbar_PrintScoreboardItem(pos, pl, (pl.sv_entnum == player_localentnum - 1));
687                         pos_y += 1.25 * sbar_fontsize_y;
688                         tmp_y -= 1.25 * sbar_fontsize_y;
689                 }
690
691                 // rgb := tempvector :)
692                 rgb = pos + '0 1.5 0' * sbar_fontsize_y;
693                 pos_y += 3 * sbar_fontsize_y;
694                 specs = 0;
695                 for(pl = players.sort_next; pl; pl = pl.sort_next)
696                 {
697                         if(pl.team != COLOR_SPECTATOR)
698                                 continue;
699                         Sbar_PrintScoreboardItem(pos, pl, (pl.sv_entnum == player_localentnum - 1));
700                         pos += '0 1.25 0' * sbar_fontsize_y;
701                         ++specs;
702                 }
703                         
704                 if(specs)
705                         drawstring(rgb, "Spectators", sbar_fontsize, '1 1 1', 1, 0);
706         }
707         sbar = sbar_save;
708 }
709
710 void Sbar_Score(float margin)
711 {
712         float timelimit, timeleft, minutes, seconds, distribution, myplace, score;
713         vector sbar_save, place;
714         entity tm, pl, me;
715         sbar_save = sbar;
716
717         sbar_y = vid_conheight - (32+12);
718         sbar_x -= margin;
719         
720         place = '-48 -12 0';
721         if(teamplay)
722         {
723                 // Layout:
724                 //
725                 //   team1 team3 team4
726                 //
727                 //         TEAM2
728                 //for(i = 0; i < 4; ++i)
729                 for(tm = teams.sort_next; tm; tm = tm.sort_next)
730                 {
731                         if(tm.team == COLOR_SPECTATOR || !tm.team_size) // no players? don't display
732                                 continue;
733                         // -32*4 = -128
734                         score = tm.(teamscores[ts_primary]);
735                         if(tm.team == myteam)
736                                 Sbar_DrawXNum('-128 0', score, 4, 32, GetTeamRGB(tm.team), 1, DRAWFLAG_NORMAL);
737                         else
738                         {
739                                 Sbar_DrawXNum(place, score, 4, 12, GetTeamRGB(tm.team), 1, DRAWFLAG_NORMAL);
740                                 place_x -= 4*12;
741                         }
742                 }
743         } else {
744                 // me vector := [team/connected frags id]
745                 myplace = 0;
746                 for(me = players.sort_next; me; me = me.sort_next)
747                 {
748                         if(me.team != COLOR_SPECTATOR)
749                                 ++myplace;
750                         if(me.sv_entnum == player_localentnum - 1)
751                                 break;
752                 }
753                 pl = players;
754                 if(pl == me)
755                         pl = pl.sort_next;
756                 
757                 if(pl) {
758                         distribution = me.(scores[ps_primary]) - pl.(scores[ps_primary]);
759                 } else
760                         distribution = 0;
761                 
762                 if(myplace == 1)
763                         Sbar_DrawXNum('-36 -12', myplace, 3, 12, '1 1 1', 1, DRAWFLAG_NORMAL);
764                 else if(myplace == 2)
765                         Sbar_DrawXNum('-36 -12', myplace, 3, 12, '1 1 0', 1, DRAWFLAG_NORMAL);
766                 else
767                         Sbar_DrawXNum('-36 -12', myplace, 3, 12, '1 0 0', 1, DRAWFLAG_NORMAL);
768
769                 score = me.(scores[ps_primary]);
770                 if(distribution >= 0)
771                 {
772                         Sbar_DrawXNum('-84 -12', distribution, 4, 12, ' 1 1 1', 1, DRAWFLAG_NORMAL);
773                         Sbar_DrawXNum('-128 0', score, 4, 32, '1 1 1', 1, DRAWFLAG_NORMAL);
774                 } else if(distribution >= -5)
775                 {
776                         Sbar_DrawXNum('-84 -12', distribution, 4, 12, ' 1 1 0', 1, DRAWFLAG_NORMAL);
777                         Sbar_DrawXNum('-128 0', score, 4, 32, '1 1 0', 1, DRAWFLAG_NORMAL);
778                 } else {
779                         Sbar_DrawXNum('-84 -12', distribution, 4, 12, ' 1 0 0', 1, DRAWFLAG_NORMAL);
780                         Sbar_DrawXNum('-128 0', score, 4, 32, '1 0 0', 1, DRAWFLAG_NORMAL);
781                 }
782         }
783         timelimit = getstatf(STAT_TIMELIMIT);
784         if(timelimit)
785         {
786                 timeleft = max(0, timelimit * 60 - time);
787                 minutes = floor(timeleft / 60);
788                 seconds = floor(timeleft - minutes*60);
789                 if(minutes >= 5)
790                 {
791                         Sbar_DrawXNum('-72 32', minutes, 3, 12, '1 1 1', 1, DRAWFLAG_NORMAL);
792                         drawpic(sbar + '-36 32', "gfx/num_colon", '12 12', '1 1 1', sbar_alpha_fg, 0);
793                         Sbar_DrawXNum('-24 32', seconds, -2, 12, '1 1 1', 1, DRAWFLAG_NORMAL);
794                 } else if(minutes >= 1)
795                 {
796                         Sbar_DrawXNum('-72 32', minutes, 3, 12, '1 1 0', 1, DRAWFLAG_NORMAL);
797                         drawpic(sbar + '-36 32', "gfx/num_colon", '12 12', '1 1 0', sbar_alpha_fg, 0);
798                         Sbar_DrawXNum('-24 32', seconds, -2, 12, '1 1 0', 1, DRAWFLAG_NORMAL);
799                 } else {
800                         Sbar_DrawXNum('-24 32', seconds, -2, 12, '1 0 0', 1, DRAWFLAG_NORMAL);
801                 }
802         } else {
803                 minutes = floor(time / 60);
804                 seconds = floor(time - minutes*60);
805                 Sbar_DrawXNum('-72 32', minutes, 3, 12, '1 1 1', 1, DRAWFLAG_NORMAL);
806                 drawpic(sbar + '-36 32', "gfx/num_colon", '12 12', '1 1 1', sbar_alpha_fg, 0);
807                 Sbar_DrawXNum('-24 32', seconds, -2, 12, '1 1 1', 1, DRAWFLAG_NORMAL);
808         }
809         sbar = sbar_save;
810 }
811
812 void Sbar_MiniscoreItem(vector pos, entity pl, float is_self)
813 {
814         float x, score;
815         pos_x += 72;
816         
817         if(teamplay)
818                 drawfill(pos + '0 1 0', '40 6 0', GetTeamRGB(pl.team)*0.5, 1, DRAWFLAG_NORMAL);
819         else
820                 drawfill(pos + '0 1 0', '40 6 0', '0.5 0.5 0.5', 0.5, DRAWFLAG_NORMAL);
821         x = pos_x;
822         pos_x += 5*8;
823         score = pl.(scores[ps_primary]);
824         pos_x -= stringwidth(ftos(score), FALSE)*8;
825         drawstring(pos, ftos(score), '8 8 0', '1 1 1', 1, DRAWFLAG_NORMAL);
826         pos_x = x;
827         if(is_self)
828         {
829                 pos_x += 48;
830                 drawstring(pos, "\x0D", '8 8 0', '1 1 1', 1, DRAWFLAG_NORMAL);
831                 pos_x += 8;
832         } else
833                 pos_x += 56;
834         drawcolorcodedstring(pos, getplayerkey(pl.sv_entnum, "name"), '8 8 0', 1, 0);
835 }
836
837 void Sbar_MiniscoreTeamItem(vector pos, float color, float frags, float is_self)
838 {
839         float x;
840         pos_x += 72;
841         
842         if(teamplay)
843                 drawfill(pos + '0 1 0', '40 6 0', GetTeamRGB(color)*0.5, 1, DRAWFLAG_NORMAL);
844         else
845                 drawfill(pos + '0 1 0', '40 6 0', '0.5 0.5 0.5', 0.5, DRAWFLAG_NORMAL);
846         x = pos_x;
847         pos_x += 5*8;
848         pos_x -= stringwidth(ftos(frags), FALSE)*8;
849         drawstring(pos, ftos(frags), '8 8 0', '1 1 1', 1, DRAWFLAG_NORMAL);
850         pos_x = x;
851         if(is_self)
852         {
853                 pos_x += 48;
854                 drawstring(pos, "\x0D", '8 8 0', '1 1 1', 1, DRAWFLAG_NORMAL);
855                 pos_x += 8;
856         } else
857                 pos_x += 56;
858         drawstring(pos, GetTeamName(color), '8 8 0', '1 1 1', 1, DRAWFLAG_NORMAL);
859 }
860
861 void Sbar_MiniDeathmatchOverlay(vector pos)
862 {
863         float numlines, up, down, score;
864         entity me, tm, pl;
865         float miniscoreboard_size;
866         miniscoreboard_size = cvar("sbar_miniscoreboard_size");
867         
868         if(miniscoreboard_size == 0)
869                 return;
870         pos_y = vid_conheight - 8;
871         
872         if(miniscoreboard_size < 0)
873                 numlines = (vid_conheight - sbar_y + 7) / 8;
874         else
875                 numlines = miniscoreboard_size;
876
877         // give up if there isn't enough room
878         if(pos_x >= vid_conwidth || pos_y >= vid_conheight || numlines < 1)
879                 return;
880
881         // me vector := [team/connected frags id]
882         for(me = players.sort_next; me; me = me.sort_next)
883         {
884                 if(me.sv_entnum == player_localentnum - 1)
885                         break;
886         }
887
888         if(teamplay)
889                 numlines -= numteams;
890
891         // figure out how many players above and below we can show
892         up = floor(numlines/2);
893         down = up;
894         if((up + down) > numlines)
895                 down = numlines - up;
896
897         // render bottom-up
898         for(pl = me.sort_next; pl && down > 0; pl = pl.sort_next)
899         {
900                 if(pl.team == COLOR_SPECTATOR)
901                         continue;
902                 Sbar_MiniscoreItem(pos, pl, false);
903                 pos_y -= 9;
904                 --down;
905         }
906         Sbar_MiniscoreItem(pos, me, true);
907         pos_y -= 9;
908         up += down; // if there weren't enough lines below... add them
909         for(pl = me.sort_prev; pl && up > 0; pl = pl.sort_prev)
910         {
911                 if(pl.team == COLOR_SPECTATOR)
912                         continue;
913                 Sbar_MiniscoreItem(pos, pl, false);
914                 pos_y -= 9;
915                 --up;
916         }
917
918         if(teamplay)
919         {
920                 for(tm = teams.sort_next; tm.sort_next; tm = tm.sort_next);
921                 for(; tm; tm = tm.sort_prev)
922                 {
923                         if(!tm.team_size || tm.team == COLOR_SPECTATOR)
924                                 continue;
925                         score = tm.(teamscores[ts_primary]);
926                         Sbar_MiniscoreTeamItem(pos, tm.team, score, (tm.team == me.team));
927                         pos_y -= 9;
928                 }
929         }
930 }
931
932 void Sbar_Draw (void)
933 {
934         float i;
935         float x, fade;
936         float stat_items;
937
938         //Sbar_SortFrags();
939         Sbar_UpdatePlayerTeams();
940
941         sb_lines = 24;
942         
943         if (sb_showscores)
944                 Sbar_DrawScoreboard();
945         else if (intermission == 1)
946         {
947                 Sbar_DrawScoreboard();
948                 return;
949         }
950         else if (intermission == 2)
951                 Sbar_FinaleOverlay();
952         else
953         {
954                 if (sb_showscores || (getstati(STAT_HEALTH) <= 0 && cvar("cl_deathscoreboard")))
955                 {
956                         sbar_x = (vid_conwidth - 640.0)*0.5;
957                         sbar_y = vid_conheight - 47;
958                         //Sbar_DrawAlphaPic (sbar_x, sbar_y, sb_scorebar, sbar_alpha_bg.value);
959                         //drawpic('0 0', "gfx/scorebar", '0 0 0', '1 1 1', cvar("sbar_alpha_bg"), 0);
960                         Sbar_DrawScoreboard ();
961                 }
962                 else
963                 {
964                         if (sb_lines && sbar_hudselector == 1)
965                         {
966                                 stat_items = getstati(STAT_ITEMS);
967
968                                 sbar_x = (vid_conwidth - 320.0)*0.5;
969                                 sbar_y = vid_conheight - 24.0 - 16.0;
970                                 sbar_z = 0;
971                         
972                                 fade = 3.2 - 2 * (time - weapontime);
973                                 fade = bound(0.7, fade, 1);
974
975                                 x = 1.0;
976                                 for(i = 0; i < 8; ++i)
977                                 {
978                                         if(stat_items & x)
979                                         {
980                                                 Sbar_DrawWeapon(i+1, fade, (i + 2 == activeweapon));
981                                         }
982                                         x *= 2;
983                                 }
984                                 x *= 2*2*2*2;
985                                 if(stat_items & x)
986                                 {
987                                         Sbar_DrawWeapon(0, fade, (activeweapon == 1));
988                                 }
989
990                                 // armor
991                                 x = getstati(STAT_ARMOR);
992                                 if (x > 0)
993                                 {
994                                         // "gfx/sb_armor"
995                                         //Sbar_DrawStretchPic (72, 0, sb_armor[0], sbar_alpha_fg.value, 24, 24);
996                                         drawpic(sbar + '72 0', "gfx/sb_armor", '24 24 0', '1 1 1', sbar_alpha_fg, 0);
997                                         if(x > 200)
998                                                 Sbar_DrawXNum('0 0', x, 3, 24, '0 1 0', 1, 0);
999                                         else if(x > 100)
1000                                                 Sbar_DrawXNum('0 0', x, 3, 24, '0.2 1 0', 1, 0);
1001                                         else if(x > 50)
1002                                                 Sbar_DrawXNum('0 0', x, 3, 24, '0.6 0.7 0.8', 1, 0);
1003                                         else if(x > 25)
1004                                                 Sbar_DrawXNum('0 0', x, 3, 24, '1 1 0.2', 1, 0);
1005                                         else
1006                                                 Sbar_DrawXNum('0 0', x, 3, 24, '0.7 0 0', 1, 0);
1007                                 }
1008
1009                                 // health
1010                                 x = getstati(STAT_HEALTH);
1011                                 if (x != 0)
1012                                 {
1013                                         // "gfx/sb_health"
1014                                         //Sbar_DrawStretchPic (184, 0, sb_health, sbar_alpha_fg.value, 24, 24);
1015                                         drawpic(sbar + '184 0', "gfx/sb_health", '24 24 0', '1 1 1', sbar_alpha_fg, 0);
1016                                         if(x > 200)
1017                                                 Sbar_DrawXNum('112 0', x, 3, 24, '0 1 0', 1, 0);
1018                                         else if(x > 100)
1019                                                 Sbar_DrawXNum('112 0', x, 3, 24, '0.2 1 0', 1, 0);
1020                                         else if(x > 50)
1021                                                 Sbar_DrawXNum('112 0', x, 3, 24, '0.6 0.7 0.8', 1, 0);
1022                                         else if(x > 25)
1023                                                 Sbar_DrawXNum('112 0', x, 3, 24, '1 1 0.2', 1, 0);
1024                                         else
1025                                                 Sbar_DrawXNum('112 0', x, 3, 24, '0.7 0 0', 1, 0);
1026                                 }
1027
1028                                 // ammo
1029                                 x = getstati(STAT_AMMO);
1030                                 if ((stat_items & (NEX_IT_SHELLS | NEX_IT_BULLETS | NEX_IT_ROCKETS | NEX_IT_CELLS)) || x != 0)
1031                                 {
1032                                         if (stat_items & NEX_IT_SHELLS)
1033                                                 drawpic(sbar + '296 0', "gfx/sb_shells", '24 24 0', '1 1 1', sbar_alpha_fg, 0);
1034                                         else if (stat_items & NEX_IT_BULLETS)
1035                                                 drawpic(sbar + '296 0', "gfx/sb_bullets", '24 24 0', '1 1 1', sbar_alpha_fg, 0);
1036                                         else if (stat_items & NEX_IT_ROCKETS)
1037                                                 drawpic(sbar + '296 0', "gfx/sb_rocket", '24 24 0', '1 1 1', sbar_alpha_fg, 0);
1038                                         else if (stat_items & NEX_IT_CELLS)
1039                                                 drawpic(sbar + '296 0', "gfx/sb_cells", '24 24 0', '1 1 1', sbar_alpha_fg, 0);
1040                                         if(x > 10)
1041                                                 Sbar_DrawXNum('224 0', x, 3, 24, '0.6 0.7 0.8', 1, 0);
1042                                         else
1043                                                 Sbar_DrawXNum('224 0', x, 3, 24, '0.7 0 0', 1, 0);
1044                                 }
1045
1046                                 if (sbar_x + 320 + 160 <= vid_conwidth)
1047                                         Sbar_MiniDeathmatchOverlay(sbar + '320 0');
1048                                 if (sbar_x > 0)
1049                                         Sbar_Score(16);
1050                                 // The margin can be at most 8 to support 640x480 console size:
1051                                 //   320 + 2 * (144 + 16) = 640
1052                         }
1053                         else if (sb_lines)
1054                         {
1055                         
1056                                 stat_items = getstati(STAT_ITEMS);
1057                         
1058                                 sbar_x = (vid_conwidth - 640.0)*0.5;
1059                                 sbar_y = vid_conheight - 47;
1060                                 sbar_z = 0;
1061
1062                                 fade = 3 - 2 * (time - weapontime);
1063
1064                                 x = 1.0;
1065                                 for(i = 0; i < 8; ++i)
1066                                 {
1067                                         if(stat_items & x)
1068                                         {
1069                                                 Sbar_DrawWeapon(i+1, fade, (i + 2 == activeweapon));
1070                                         }
1071                                         x *= 2;
1072                                 }
1073                                 x *= 2*2*2*2;
1074                                 if(stat_items & x)
1075                                 {
1076                                         Sbar_DrawWeapon(0, fade, (activeweapon == 1));
1077                                 }
1078
1079                                 if (sb_lines > 24)
1080                                         drawpic(sbar, "gfx/sbar", '0 0 0', '1 1 1', sbar_alpha_fg, 0);
1081                                 else
1082                                         drawpic(sbar, "gfx/sbar_minimal", '0 0 0', '1 1 1', sbar_alpha_fg, 0);
1083
1084                                 // armor
1085                                 // (340-3*24) = 268
1086                                 Sbar_DrawXNum('268 12', getstati(STAT_ARMOR), 3, 24, '0.6 0.7 0.8', 1, 0);
1087
1088                                 // health
1089                                 // (154-3*24) = 82
1090                                 x = getstati(STAT_HEALTH);
1091                                 if(x > 100)
1092                                         Sbar_DrawXNum('82 12', x, 3, 24, '1 1 1', 1, 0);
1093                                 else if(x <= 25 && time - floor(time) > 0.5)
1094                                         Sbar_DrawXNum('82 12', x, 3, 24, '0.7 0 0', 1, 0);
1095                                 else
1096                                         Sbar_DrawXNum('81 12', x, 3, 24, '0.6 0.7 0.8', 1, 0);
1097
1098                                 // AK dont draw ammo for the laser
1099                                 x = getstati(STAT_AMMO);
1100                                 if(activeweapon != 12)
1101                                 {
1102                                         // (519-3*24) = 447
1103                                         if (stat_items & NEX_IT_SHELLS)
1104                                                 drawpic(sbar + '519 0', "gfx/sb_shells", '0 0 0', '1 1 1', sbar_alpha_fg, 0);
1105                                         else if (stat_items & NEX_IT_BULLETS)
1106                                                 drawpic(sbar + '519 0', "gfx/sb_bullets", '0 0 0', '1 1 1', sbar_alpha_fg, 0);
1107                                         else if (stat_items & NEX_IT_ROCKETS)
1108                                                 drawpic(sbar + '519 0', "gfx/sb_rocket", '0 0 0', '1 1 1', sbar_alpha_fg, 0);
1109                                         else if (stat_items & NEX_IT_CELLS)
1110                                                 drawpic(sbar + '519 0', "gfx/sb_cells", '0 0 0', '1 1 1', sbar_alpha_fg, 0);
1111                                         if(x > 10)
1112                                                 Sbar_DrawXNum('447 12', x, 3, 24, '0.6 0.7 0.8', 1, 0);
1113                                         else
1114                                                 Sbar_DrawXNum('447 12', x, 3, 24, '0.7 0 0', 1, 0);
1115                                 }
1116
1117                                 if (sb_lines > 24)
1118                                         drawpic(sbar, "gfx/sbar_overlay", '0 0 0', '1 1 1', 1, DRAWFLAG_MODULATE);
1119
1120                                 if (sbar_x + 600 + 160 <= vid_conwidth)
1121                                         Sbar_MiniDeathmatchOverlay (sbar + '600 0');
1122
1123                                 if (sbar_x > 0)
1124                                         Sbar_Score(-16);
1125                                 // Because:
1126                                 //   Mini scoreboard uses 12*4 per other team, that is, 144
1127                                 //   pixels when there are four teams...
1128                                 //   Nexuiz by default sets vid_conwidth to 800... makes
1129                                 //   sbar_x == 80...
1130                                 //   so we need to shift it by 64 pixels to the right to fit
1131                                 //   BUT: then it overlaps with the image that gets drawn
1132                                 //   for viewsize 100! Therefore, just account for 3 teams,
1133                                 //   that is, 96 pixels mini scoreboard size, needing 16 pixels
1134                                 //   to the right!
1135                         }
1136                 
1137                 
1138                         if(gametype == GAME_KEYHUNT)
1139                         {
1140                                 CSQC_kh_hud();
1141                         } else if(gametype == GAME_CTF)
1142                         {
1143                                 CSQC_ctf_hud();
1144                         }
1145                 }
1146         }
1147 }
1148
1149 void CSQC_ctf_hud(void)
1150 {
1151         // cvar("sbar_flagstatus_right") move the flag icons right
1152         // cvar("sbar_flagstatus_pos") pixel position of the nexuiz flagstatus icons
1153         float redflag, blueflag;
1154         float stat_items;
1155         vector pos;
1156         
1157         stat_items = getstati(STAT_ITEMS);
1158         redflag = (stat_items/32768) & 3;
1159         blueflag = (stat_items/131072) & 3;
1160
1161         /**
1162          * FTEQCC BUG!
1163          * For some reason now not even THAT works there...
1164          * Maybe the minus' precedence screws it up? The last one there, maybe I should use brackets
1165          **
1166          * pos_x = (cvar("sbar_flagstatus_right")) ? vid_conwidth - 10 - sbar_x - 64 : 10 - sbar_x;
1167          ** Should try those later:
1168          * pos_x = (cvar("sbar_flagstatus_right")) ? (vid_conwidth - 10 - sbar_x - 64) : (10 - sbar_x);
1169          * pos_x = ( (cvar("sbar_flagstatus_right")) ? vid_conwidth - 10 - 64 : 10 ) - sbar_x;
1170          */
1171         
1172         if(cvar("sbar_flagstatus_right"))
1173                 pos_x = vid_conwidth - 10 - sbar_x - 64;
1174         else
1175                 pos_x = 10 - sbar_x;
1176         
1177         pos_z = 0;
1178
1179         if(sbar_hudselector == 1)
1180                 pos_y = (vid_conheight - sbar_y) - cvar("sbar_flagstatus_pos") - 64;
1181         else
1182                 pos_y = -117;
1183
1184         pos += sbar;
1185
1186         switch(redflag)
1187         {
1188         case 1: drawpic(pos, "gfx/sb_flag_red_taken", '0 0 0', '1 1 1', 1, DRAWFLAG_NORMAL); break;
1189         case 2: drawpic(pos, "gfx/sb_flag_red_lost", '0 0 0', '1 1 1', 1, DRAWFLAG_NORMAL); break;
1190         case 3: drawpic(pos, "gfx/sb_flag_red_carrying", '0 0 0', '1 1 1', 1, DRAWFLAG_NORMAL); break;
1191         }
1192
1193         pos_y -= 64;
1194         
1195         switch(blueflag)
1196         {
1197         case 1: drawpic(pos, "gfx/sb_flag_blue_taken", '0 0 0', '1 1 1', 1, 0); break;
1198         case 2: drawpic(pos, "gfx/sb_flag_blue_lost", '0 0 0', '1 1 1', 1, 0); break;
1199         case 3: drawpic(pos, "gfx/sb_flag_blue_carrying", '0 0 0', '1 1 1', 1, 0); break;
1200         }
1201 }