]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/client/sbar.qc
use the old Nex model and old Nex shot origin; make csqc aware of the weapon impulses...
[divverent/nexuiz.git] / data / qcsrc / client / sbar.qc
1
2 float last_weapon;
3 float weapontime;
4
5 float sb_lines; // still don't know what to do with that NOTE: check dp's sbar.c to see what that should be
6
7 vector sbar;
8 float sbar_alpha_fg;
9 float sbar_hudselector;
10
11 float ps_primary, ps_secondary;
12 float ts_primary, ts_secondary;
13
14 void CSQC_kh_hud();
15 void CSQC_ctf_hud();
16 void MapVote_Draw();
17 void Sbar_FinaleOverlay()
18 {
19         /*vector pos;
20         pos_x = (vid_conwidth - 1)/2;
21         pos_y = 16;
22         pos_z = 0;*/
23         
24         //drawpic(pos, "gfx/finale", '0 0 0', '1 1 1', sbar_alpha_fg, DRAWFLAG_NORMAL);
25         
26         //drawstring(pos, "END", sbar_fontsize, '1 1 1', 1, DRAWFLAG_NORMAL);
27         MapVote_Draw();
28 }
29
30 float weaponspace[10];
31 void Sbar_DrawWeapon_Clear()
32 {
33         float idx;
34         for(idx = 0; idx < 10; ++idx)
35                 weaponspace[idx] = 0;
36 }
37 void Sbar_DrawWeapon(float nr, float fade, float active)
38 {
39         vector pos, vsize, color;
40         float value, idx, imp, sp;
41
42         imp = weaponimpulse[nr];
43         if(imp == 0)
44                 idx = 9;
45         else
46                 idx = imp - 1;
47         
48         value = (active) ? 1 : 0.6;
49         color_x = color_y = color_z = value;
50         
51         if(sbar_hudselector == 1)
52         {
53                 // width = 300, height = 100
54                 const float w_width = 32, w_height = 12, w_space = 2, font_size = 8;
55
56                 sp = weaponspace[idx] + 1;
57                 weaponspace[idx] = sp;
58                 
59                 pos_x = (vid_conwidth - w_width * 9) * 0.5 + w_width * idx;
60                 pos_y = (vid_conheight - w_height * sp);
61                 pos_z = 0;
62                 vsize_x = w_width;
63                 vsize_y = w_height;
64                 vsize_z = 0;
65                 drawpic(pos, strcat("gfx/inv_weapon", ftos(nr)), vsize, color, value * fade * sbar_alpha_fg, 0);
66                 pos_x += w_space;
67                 pos_y += w_space;
68                 vsize_x = font_size;
69                 vsize_y = font_size;
70                 vsize_z = 0;
71                 drawstring(pos, ftos(imp), vsize, '1 1 0', sbar_alpha_fg, 0);
72         }
73         else
74         {
75                 // width = 300, height = 100
76                 const float w2_width = 300, w2_height = 100, w2_space = 10;
77                 const float w2_scale = 0.4;
78
79                 pos_x = vid_conwidth - (w2_width + w2_space) * w2_scale;
80                 pos_y = (w2_height + w2_space) * w2_scale * nr + w2_space;
81                 pos_z = 0;
82                 vsize_x = w2_width * w2_scale;
83                 vsize_y = w2_height * w2_scale;
84                 vsize_z = 0;
85                 
86                 drawpic(pos, strcat("gfx/inv_weapon", ftos(nr)), vsize, color, value * fade * sbar_alpha_fg, 0);
87         }
88 }
89 void Sbar_DrawXNum (vector pos, float num, float digits, float lettersize, vector rgb, float a, float dflags)
90 {
91         float l, i;
92         string str, tmp;
93         float minus;
94         vector vsize;
95
96         vsize_x = vsize_y = lettersize;
97         vsize_z = 0;
98
99         if(num < 0)
100         {
101                 minus = true;
102                 num = -num;
103                 pos_x -= lettersize;
104         } else
105                 minus = false;
106         
107         if(digits < 0)
108         {
109                 tmp = ftos(num);
110                 digits = -digits;
111                 str = strcat(substring("0000000000", 0, digits - strlen(tmp)), tmp);
112         } else
113                 str = ftos(num);
114         
115         l = strlen(str);
116
117         if(l > digits)
118         {
119                 str = substring(str, l-digits, 999);
120                 l = strlen(str);
121         } else if(l < digits)
122                 pos_x += (digits-l) * lettersize;
123
124         if(minus)
125         {
126                 drawpic(sbar + pos, "gfx/num_minus", vsize, rgb, a * sbar_alpha_fg, dflags);
127                 pos_x += lettersize;
128         }
129
130         for(i = 0; i < l; ++i)
131         {
132                 drawpic(sbar + pos, strcat("gfx/num_", substring(str, i, 1)), vsize, rgb, a * sbar_alpha_fg, dflags);
133                 pos_x += lettersize;
134         }
135 }
136
137 void Cmd_Sbar_SetFields(float argc);
138 void Sbar_InitScores()
139 {
140         float i, f;
141
142         ps_primary = ps_secondary = ts_primary = ts_secondary = -1;
143         for(i = 0; i < MAX_SCORE; ++i)
144         {
145                 f = (scores_flags[i] & SFL_SORT_PRIO_MASK);
146                 if(f == SFL_SORT_PRIO_PRIMARY)
147                         ps_primary = i;
148                 if(f == SFL_SORT_PRIO_SECONDARY)
149                         ps_secondary = i;
150         }
151         if(ps_secondary == -1)
152                 ps_secondary = ps_primary;
153         
154         for(i = 0; i < MAX_TEAMSCORE; ++i)
155         {
156                 f = (teamscores_flags[i] & SFL_SORT_PRIO_MASK);
157                 if(f == SFL_SORT_PRIO_PRIMARY)
158                         ts_primary = i;
159                 if(f == SFL_SORT_PRIO_SECONDARY)
160                         ts_secondary = i;
161         }
162         if(ts_secondary == -1)
163                 ts_secondary = ts_primary;
164
165         Cmd_Sbar_SetFields(0);
166 }
167
168 void Sbar_UpdatePlayerPos(entity pl);
169 float SetTeam(entity pl, float Team);
170 //float lastpnum;
171 void Sbar_UpdatePlayerTeams()
172 {
173         float Team;
174         entity pl, tmp;
175         float num;
176
177         num = 0;
178         for(pl = players.sort_next; pl; pl = pl.sort_next)
179         {
180                 num += 1;
181                 Team = GetPlayerColor(pl.sv_entnum);
182                 if(SetTeam(pl, Team))
183                 {
184                         tmp = pl.sort_prev;
185                         Sbar_UpdatePlayerPos(pl);
186                         if(tmp)
187                                 pl = tmp;
188                         else
189                                 pl = players.sort_next;
190                 }
191         }
192         /*
193         if(num != lastpnum)
194                 print(strcat("PNUM: ", ftos(num), "\n"));
195         lastpnum = num;
196         */
197 }
198
199 float Sbar_ComparePlayerScores(entity left, entity right)
200 {
201         float vl, vr;
202         vl = GetPlayerColor(left.sv_entnum);
203         vr = GetPlayerColor(right.sv_entnum);
204         
205         if(vl > vr)
206                 return true;
207         if(vl < vr)
208                 return false;
209
210         if(vl == COLOR_SPECTATOR)
211         {
212                 // FIRST the one with scores (spectators), THEN the ones without (downloaders)
213                 // no other sorting
214                 if(!left.gotscores && right.gotscores)
215                         return true;
216                 return false;
217         }
218
219         vl = left.scores[ps_primary];
220         vr = right.scores[ps_primary];
221         if(scores_flags[ps_primary] & SFL_ZERO_IS_WORST)
222         {
223                 if(vl == 0 && vr != 0)
224                         return 1;
225                 if(vl != 0 && vr == 0)
226                         return 0;
227         }
228         if(vl > vr)
229                 return IS_INCREASING(scores_flags[ps_primary]);
230         if(vl < vr)
231                 return IS_DECREASING(scores_flags[ps_primary]);
232         
233         vl = left.scores[ps_secondary];
234         vr = right.scores[ps_secondary];
235         if(scores_flags[ps_secondary] & SFL_ZERO_IS_WORST)
236         {
237                 if(vl == 0 && vr != 0)
238                         return 1;
239                 if(vl != 0 && vr == 0)
240                         return 0;
241         }
242         if(vl > vr)
243                 return IS_INCREASING(scores_flags[ps_secondary]);
244         if(vl < vr)
245                 return IS_DECREASING(scores_flags[ps_secondary]);
246         
247         return false;
248 }
249
250 void Sbar_UpdatePlayerPos(entity player)
251 {
252         for(other = player.sort_next; other && Sbar_ComparePlayerScores(player, other); other = player.sort_next)
253         {
254                 SORT_SWAP(player, other);
255         }
256         for(other = player.sort_prev; other != players && Sbar_ComparePlayerScores(other, player); other = player.sort_prev)
257         {
258                 SORT_SWAP(other, player);
259         }
260 }
261
262 float Sbar_CompareTeamScores(entity left, entity right)
263 {
264         float vl, vr;
265
266         if(left.team == COLOR_SPECTATOR)
267                 return 1;
268         if(right.team == COLOR_SPECTATOR)
269                 return 0;
270         
271         vl = left.teamscores[ts_primary];
272         vr = right.teamscores[ts_primary];
273         if(vl > vr)
274                 return IS_INCREASING(teamscores_flags[ts_primary]);
275         if(vl < vr)
276                 return IS_DECREASING(teamscores_flags[ts_primary]);
277         
278         vl = left.teamscores[ts_secondary];
279         vr = right.teamscores[ts_secondary];
280         if(vl > vr)
281                 return IS_INCREASING(teamscores_flags[ts_secondary]);
282         if(vl < vr)
283                 return IS_DECREASING(teamscores_flags[ts_secondary]);
284
285         return false;
286 }
287
288 void Sbar_UpdateTeamPos(entity Team)
289 {
290         for(other = Team.sort_next; other && Sbar_CompareTeamScores(Team, other); other = Team.sort_next)
291         {
292                 SORT_SWAP(Team, other);
293         }
294         for(other = Team.sort_prev; other != teams && Sbar_CompareTeamScores(other, Team); other = Team.sort_prev)
295         {
296                 SORT_SWAP(other, Team);
297         }
298 }
299
300 void Cmd_Sbar_Help(float argc)
301 {
302         print("You can modify the scoreboard using the\n");
303         print("^3|---------------------------------------------------------------|\n");
304         print("^1 TO BE DONE\n");
305         print("Usage:\n");
306         print("^2sbar_columns_set default\n");
307         print("^2sbar_columns_set ^7filed1 field2 ...\n");
308         print("The following field names are recognized (case INsensitive):\n");
309         print("You can use a ^3|^7 to start the right-aligned fields.\n");
310         
311         print("^3name^7 or ^3nick^7             Name of a player\n");
312         print("^3ping^7                     Ping time\n\n");
313         print("^3kd^7 or ^3kdr^7 or ^3kdratio^7 or ^3k/d\n");
314         print("                         The kill-death ratio\n");
315
316         print("Before a field you can put a + or - sign, then a comma separated list\n");
317         print("of game types, then a slash, to make the field show up only in these\n");
318         print("or in all but these game types.\n");
319
320         local float i;
321         print("Additional columns:\n");
322         for(i = 0; i < MAX_SCORE; ++i)
323         {
324                 if(scores_label[i] != "")
325                         print(strcat(scores_label[i], "\n"));
326         }
327 }
328
329 #define MIN_NAMELEN 24
330 #define MAX_NAMELEN 24
331
332 string Sbar_DefaultColumnLayout()
333 {
334         return "ping pl name | -ctf,kh/kills -ctf,kh/deaths +lms/lives +lms/rank +kh,ctf/caps +kh/pushes +kh/destroyed -lms/score";
335 }
336
337 void Cmd_Sbar_SetFields(float argc)
338 {
339         float i, j, slash;
340         string str, pattern, subpattern;
341         float digit;
342         float have_name, have_primary, have_secondary, have_separator;
343         float missing;
344
345         // TODO: re enable with gametype dependant cvars?
346         if(argc < 2) // no arguments provided
347                 argc = tokenizebyseparator(strcat("x ", cvar_string("sbar_columns")), " ");
348
349         if(argc < 2)
350                 argc = tokenizebyseparator(strcat("x ", Sbar_DefaultColumnLayout()), " ");
351
352         if(argc == 2)
353         {
354                 if(argv(1) == "default")
355                         argc = tokenizebyseparator(strcat("x ", Sbar_DefaultColumnLayout()), " ");
356                 else if(argv(1) == "all")
357                 {
358                         string s;
359                         s = "ping pl name |";
360                         for(i = 0; i < MAX_SCORE; ++i)
361                         {
362                                 if(i != ps_primary)
363                                 if(i != ps_secondary)
364                                 if(scores_label[i] != "")
365                                         s = strcat(s, " ", scores_label[i]);
366                         }
367                         if(ps_secondary != ps_primary)
368                                 s = strcat(s, " ", scores_label[ps_secondary]);
369                         s = strcat(s, " ", scores_label[ps_primary]);
370                         argc = tokenizebyseparator(strcat("x ", s), " ");
371                 }
372         }
373                 
374         
375         argc = min(MAX_SBAR_FIELDS, argc);
376         sbar_num_fields = 0;
377
378         drawfont = sbar_font;
379         digit = stringwidth("0123456789", FALSE) / 10;
380
381         subpattern = strcat(",", GametypeNameFromType(gametype), ",");
382
383         argc = min(argc-1, MAX_SBAR_FIELDS-1);
384         for(i = 0; i < argc; ++i)
385         {
386                 str = argv(i+1);
387
388                 slash = strstrofs(str, "/", 0);
389                 if(slash >= 0)
390                 {
391                         pattern = substring(str, 0, slash);
392                         str = substring(str, slash + 1, strlen(str) - (slash + 1));
393
394                         if(substring(pattern, 0, 1) == "-")
395                         {
396                                 pattern = substring(pattern, 1, strlen(pattern) - 1);
397                                 if(strstrofs(strcat(",", pattern, ","), subpattern, 0) >= 0)
398                                         continue;
399                         }
400                         else
401                         {
402                                 if(substring(pattern, 0, 1) == "+")
403                                         pattern = substring(pattern, 1, strlen(pattern) - 1);
404                                 if(strstrofs(strcat(",", pattern, ","), subpattern, 0) < 0)
405                                         continue;
406                         }
407                 }
408
409                 strunzone(sbar_title[sbar_num_fields]);
410                 sbar_title[sbar_num_fields] = strzone(str);
411                 sbar_size[sbar_num_fields] = stringwidth(str, FALSE);
412                 str = strtolower(str);
413
414                 if(str == "ping") {
415                         sbar_field[sbar_num_fields] = SP_PING;
416                 } else if(str == "pl") {
417                         sbar_field[sbar_num_fields] = SP_PL;
418                 } else if(str == "kd" || str == "kdr" || str == "kdratio" || str == "k/d") {
419                         sbar_field[sbar_num_fields] = SP_KDRATIO;
420                 } else if(str == "name" || str == "nick") {
421                         sbar_field[sbar_num_fields] = SP_NAME;
422                         sbar_size[sbar_num_fields] = MIN_NAMELEN; // minimum size? any use?
423                         have_name = 1;
424                 } else if(str == "|") {
425                         sbar_field[sbar_num_fields] = SP_SEPARATOR;
426                         have_separator = 1;
427                 } else {
428                         for(j = 0; j < MAX_SCORE; ++j)
429                                 if(str == strtolower(scores_label[j]))
430                                         goto found; // sorry, but otherwise fteqcc -O3 miscompiles this and warns about "unreachable code"
431 :notfound
432                         print(strcat("^1Error:^7 Unknown score field: '", str, "'\n"));
433                         continue;
434 :found
435                         sbar_field[sbar_num_fields] = j;
436                         if(j == ps_primary)
437                                 have_primary = 1;
438                         if(j == ps_secondary)
439                                 have_secondary = 1;
440                 }
441                 ++sbar_num_fields;
442         }
443
444         if(scores_flags[ps_primary] & SFL_ALLOW_HIDE)
445                 have_primary = 1;
446         if(scores_flags[ps_secondary] & SFL_ALLOW_HIDE)
447                 have_secondary = 1;
448         if(ps_primary == ps_secondary)
449                 have_secondary = 1;
450         missing = !have_primary + !have_secondary + !have_separator + !have_name;
451
452         if(sbar_num_fields+missing < MAX_SBAR_FIELDS)
453         {
454                 if(!have_name)
455                 {
456                         strunzone(sbar_title[sbar_num_fields]);
457                         for(i = sbar_num_fields; i > 0; --i)
458                         {
459                                 sbar_title[i] = sbar_title[i-1];
460                                 sbar_size[i] = sbar_size[i-1];
461                                 sbar_field[i] = sbar_field[i-1];
462                         }
463                         sbar_title[0] = strzone("name");
464                         sbar_field[0] = SP_NAME;
465                         sbar_size[0] = MIN_NAMELEN; // minimum size? any use?
466                         ++sbar_num_fields;
467                         print("fixed missing field 'name'\n");
468
469                         if(!have_separator)
470                         {
471                                 strunzone(sbar_title[sbar_num_fields]);
472                                 for(i = sbar_num_fields; i > 1; --i)
473                                 {
474                                         sbar_title[i] = sbar_title[i-1];
475                                         sbar_size[i] = sbar_size[i-1];
476                                         sbar_field[i] = sbar_field[i-1];
477                                 }
478                                 sbar_title[1] = strzone("|");
479                                 sbar_field[1] = SP_SEPARATOR;
480                                 sbar_size[1] = stringwidth("|", FALSE);
481                                 ++sbar_num_fields;
482                                 print("fixed missing field '|'\n");
483                         }
484                 }
485                 else if(!have_separator)
486                 {
487                         strunzone(sbar_title[sbar_num_fields]);
488                         sbar_title[sbar_num_fields] = strzone("|");
489                         sbar_size[sbar_num_fields] = stringwidth("|", FALSE);
490                         sbar_field[sbar_num_fields] = SP_SEPARATOR;
491                         ++sbar_num_fields;
492                         print("fixed missing field '|'\n");
493                 }
494
495                 if(!have_secondary)
496                 {
497                         strunzone(sbar_title[sbar_num_fields]);
498                         sbar_title[sbar_num_fields] = strzone(scores_label[ps_secondary]);
499                         sbar_size[sbar_num_fields] = stringwidth(sbar_title[sbar_num_fields], FALSE);
500                         sbar_field[sbar_num_fields] = ps_secondary;
501                         ++sbar_num_fields;
502                         print("fixed missing field '", scores_label[ps_secondary], "'\n");
503                 }
504
505                 if(!have_primary)
506                 {
507                         strunzone(sbar_title[sbar_num_fields]);
508                         sbar_title[sbar_num_fields] = strzone(scores_label[ps_primary]);
509                         sbar_size[sbar_num_fields] = stringwidth(sbar_title[sbar_num_fields], FALSE);
510                         sbar_field[sbar_num_fields] = ps_primary;
511                         ++sbar_num_fields;
512                         print("fixed missing field '", scores_label[ps_primary], "'\n");
513                 }
514         }
515
516         sbar_field[sbar_num_fields] = SP_END;
517 }
518
519 // MOVEUP::
520 vector sbar_field_rgb;
521 string Sbar_GetField(entity pl, float field)
522 {
523         float tmp, num, denom, f;
524         string str;
525         sbar_field_rgb = '1 1 1';
526         switch(field)
527         {
528                 case SP_PING:
529                         if not(pl.gotscores)
530                                 return "\x8D\x8D\x8D"; // >>> sign
531                         str = getplayerkey(pl.sv_entnum, "ping");
532                         if(str == "0")
533                                 return "N/A";
534                         tmp = max(0, min(220, stof(str)-80)) / 220;
535                         sbar_field_rgb = '1 1 1' - '0 1 1'*tmp;
536                         return str;
537                 
538                 case SP_PL:
539                         if not(pl.gotscores)
540                                 return "N/A";
541                         str = getplayerkey(pl.sv_entnum, "pl");
542                         if(str == "0")
543                                 return "";
544                         tmp = bound(0, stof(str), 20) / 20; // 20% is REALLY BAD pl
545                         sbar_field_rgb = '1 0.5 0.5' - '0 0.5 0.5'*tmp;
546                         return str;
547                 
548                 case SP_NAME:
549                         return getplayerkey(pl.sv_entnum, "name");
550
551                 case SP_KDRATIO:
552                         num = pl.(scores[SP_KILLS]);
553                         denom = pl.(scores[SP_DEATHS]);
554
555                         if(denom == 0) {
556                                 sbar_field_rgb = '0 1 0';
557                                 str = ftos(num);
558                         } else if(num <= 0) {
559                                 sbar_field_rgb = '1 0 0';
560                                 str = ftos(num/denom);
561                         } else
562                                 str = ftos(num/denom);
563                 
564                         tmp = strstrofs(str, ".", 0);
565                         if(tmp > 0)
566                                 str = substring(str, 0, tmp+2);
567                         return str;
568                         
569                 default:
570                         tmp = pl.(scores[field]);
571                         f = scores_flags[field];
572                         if(field == ps_primary)
573                                 sbar_field_rgb = '1 1 0';
574                         else if(field == ps_secondary)
575                                 sbar_field_rgb = '0 1 1';
576                         else
577                                 sbar_field_rgb = '1 1 1';
578                         return ScoreString(f, tmp);
579         }
580         //return "error";
581 }
582
583 // shamelessly stolen from menu QC :P <- as if I would steal YOUR code pfft ;)
584 float textLengthUpToWidth(string theText, float maxWidth, float handleColors)
585 {
586         // STOP.
587         // The following function is SLOW.
588         // For your safety and for the protection of those around you...
589         // DO NOT CALL THIS AT HOME.
590         // No really, don't.
591         if(stringwidth(theText, handleColors) <= maxWidth)
592                 return strlen(theText); // yeah!
593
594         // binary search for right place to cut string
595         float left, right, middle; // this always works
596         left = 0;
597         right = strlen(theText); // this always fails
598         do
599         {
600                 middle = floor((left + right) / 2);
601                 if(stringwidth(substring(theText, 0, middle), handleColors) <= maxWidth)
602                         left = middle;
603                 else
604                         right = middle;
605         }
606         while(left < right - 1);
607
608         // NOTE: when color codes are involved, this binary search is,
609         // mathematically, BROKEN. However, it is obviously guaranteed to
610         // terminate, as the range still halves each time - but nevertheless, it is
611         // guaranteed that it finds ONE valid cutoff place (where "left" is in
612         // range, and "right" is outside).
613
614         return left;
615 }
616 string textShortenToWidth(string theText, float maxWidth, float handleColors)
617 {
618         if(stringwidth(theText, handleColors) <= maxWidth)
619                 return theText;
620         else
621                 return strcat(substring(theText, 0, textLengthUpToWidth(theText, maxWidth - stringwidth("...", handleColors), handleColors)), "...");
622 }
623
624 float xmin, xmax, ymin, ymax, sbwidth, sbheight;
625
626 void Sbar_PrintScoreboardItem(vector pos, entity pl, float is_self)
627 {
628         vector tmp;
629         string str;
630         float i, field, len;
631         float is_spec;
632         is_spec = (GetPlayerColor(pl.sv_entnum) == COLOR_SPECTATOR);
633
634         // Layout:
635         tmp_z = 0;
636         if(is_self)
637         {
638                 tmp_x = sbwidth;
639                 tmp_y = sbar_fontsize_y;
640                 drawfill(pos - '1 1 0', tmp + '2 2 0', '1 1 1', 0.3, DRAWFLAG_NORMAL);
641         }       
642         tmp_y = 0;
643         
644         for(i = 0; i < sbar_num_fields; ++i)
645         {
646                 field = sbar_field[i];
647                 if(field == SP_SEPARATOR)
648                         break;
649
650                 if(is_spec && field != SP_NAME && field != SP_PING) {
651                         pos_x += sbar_fontsize_x*sbar_size[i] + sbar_fontsize_x;
652                         continue;
653                 }
654                 str = Sbar_GetField(pl, field);
655
656                 if(field == SP_NAME)
657                 {
658                         float realsize;
659                         float j;
660                         realsize = sbar_size[i];
661                         if(i+1 < sbar_num_fields)
662                                 if(sbar_field[i+1] == SP_SEPARATOR)
663                                 {
664                                         realsize = (xmax - xmin) / sbar_fontsize_x;
665                                         for(j = 0; j < sbar_num_fields; ++j) if(j != i) if(sbar_field[j] != SP_SEPARATOR)
666                                                 realsize -= sbar_size[j] + 1;
667                                         realsize += 1;
668                                 }
669                         str = textShortenToWidth(str, realsize, TRUE);
670                 }
671                 len = stringwidth(str, TRUE);
672                 
673                 if(sbar_size[i] < len)
674                         sbar_size[i] = len;
675
676                 pos_x += sbar_fontsize_x*sbar_size[i] + sbar_fontsize_x;
677
678                 if(field == SP_NAME) {
679                         tmp_x = sbar_fontsize_x*sbar_size[i] + sbar_fontsize_x;
680                         drawcolorcodedstring(pos - tmp, str, sbar_fontsize, 1, DRAWFLAG_NORMAL);
681                 } else {
682                         tmp_x = len*sbar_fontsize_x + sbar_fontsize_x;
683                         drawstring(pos - tmp, str, sbar_fontsize, sbar_field_rgb, 1, DRAWFLAG_NORMAL);
684                 }
685         }
686         
687         if(sbar_field[i] == SP_SEPARATOR)
688         {
689                 pos_x = xmax;
690                 for(i = sbar_num_fields-1; i > 0; --i)
691                 {
692                         field = sbar_field[i];
693                         if(field == SP_SEPARATOR)
694                                 break;
695                         
696                         if(is_spec && field != SP_NAME && field != SP_PING) {
697                                 pos_x -= sbar_fontsize_x*sbar_size[i] + sbar_fontsize_x;
698                                 continue;
699                         }
700                         
701                         str = Sbar_GetField(pl, field);
702
703                         if(field == SP_NAME)
704                                 str = textShortenToWidth(str, sbar_size[i], TRUE);
705                         len = stringwidth(str, TRUE);
706
707                         if(sbar_size[i] < len)
708                                 sbar_size[i] = len;
709
710                         if(field == SP_NAME) {
711                                 tmp_x = sbar_fontsize_x*len; // left or right aligned? let's put it right...
712                                 drawcolorcodedstring(pos - tmp, str, sbar_fontsize, 1, DRAWFLAG_NORMAL);
713                         } else {
714                                 tmp_x = sbar_fontsize_x*len; //strlen(str);
715                                 drawstring(pos - tmp, str, sbar_fontsize, sbar_field_rgb, 1, DRAWFLAG_NORMAL);
716                         }
717                         pos_x -= sbar_fontsize_x*sbar_size[i] + sbar_fontsize_x;
718                 }
719         }
720 }
721
722 float lastpingstime;
723 void Sbar_DrawScoreboard()
724 {
725         //float xmin, ymin, xmax, ymax;
726         vector rgb, pos, tmp, sbar_save;
727         entity pl, tm;
728         float specs, i;
729         float center_x;
730
731         if(time > lastpingstime + 10)
732         {
733                 localcmd("pings\n");
734                 lastpingstime = time;
735         }
736
737         xmin = vid_conwidth / 5;
738         ymin = 20;
739
740         xmax = vid_conwidth - xmin;
741         ymax = vid_conheight - 0.2*vid_conheight;
742
743         sbwidth = xmax - xmin;
744         sbheight = ymax - ymin;
745
746         center_x = xmin + 0.5*sbwidth;
747
748         //Sbar_UpdateFields();
749
750         // Initializes position
751         //pos_x = xmin;
752         pos_y = ymin;
753         pos_z = 0;
754
755         // Heading
756         drawfont = sbar_bigfont;
757         pos_x = center_x - stringwidth("Scoreboard", TRUE)*0.5*24;
758         drawstring(pos, "Scoreboard", '24 24 0', '1 1 1', 1, DRAWFLAG_NORMAL);
759         pos_x = xmin;
760         pos_y += 24 + 4;
761
762         // Titlebar background:
763         tmp_x = sbwidth;
764         tmp_y = sbar_fontsize_y;
765         drawfill(pos - '1 1 0', tmp + '2 2 0', '0.5 0.5 0.5', 0.5, DRAWFLAG_NORMAL);
766         
767         drawfont = sbar_font;
768
769         for(i = 0; i < sbar_num_fields; ++i)
770         {
771                 if(sbar_field[i] == SP_SEPARATOR)
772                         break;
773                 drawstring(pos, sbar_title[i], sbar_fontsize, '1 1 1', 1, DRAWFLAG_NORMAL);
774                 pos_x += sbar_fontsize_x*sbar_size[i] + sbar_fontsize_x;
775         }
776         
777         if(sbar_field[i] == SP_SEPARATOR)
778         {
779                 pos_x = xmax + sbar_fontsize_x;
780                 tmp_y = tmp_z = 0;
781                 for(i = sbar_num_fields-1; i > 0; --i)
782                 {
783                         if(sbar_field[i] == SP_SEPARATOR)
784                                 break;
785
786                         pos_x -= sbar_fontsize_x*sbar_size[i] + sbar_fontsize_x;
787                         /**
788                          * FTEQCC BUG!
789                          * Using the following line will fuck it all up:
790                          **
791                          * tmp_x = sbar_size[i] - strlen(sbar_title[i])*8;
792                          */
793                         tmp_x = sbar_fontsize_x*sbar_size[i];
794                         tmp_x -= stringwidth(sbar_title[i], FALSE)*sbar_fontsize_x;
795                         drawstring(pos + tmp, sbar_title[i], sbar_fontsize, '1 1 1', 1, DRAWFLAG_NORMAL);
796                 }
797         }
798                 
799         pos_x = xmin;
800         pos_y += 1.5 * sbar_fontsize_y;
801
802         sbar_save = sbar;
803         sbar = '0 0 0';
804         
805         if(teamplay)
806         {
807                 //for(tm = sortedTeams.sort_next; tm; tm = tm.sort_next)
808                 for(tm = teams.sort_next; tm; tm = tm.sort_next)
809                 {
810                         if(!tm.team_size || tm.team == COLOR_SPECTATOR)
811                                 continue;
812
813                         rgb = GetTeamRGB(tm.team);
814
815                         pos_x = xmin;
816
817                         Sbar_DrawXNum(
818                                 pos - '6.5 0 0' * sbar_fontsize_y,
819                                 tm.(teamscores[ts_primary]),
820                                 4, sbar_fontsize_y * 1.5, rgb, 1, DRAWFLAG_NORMAL);
821
822                         if(ts_primary != ts_secondary)
823                         Sbar_DrawXNum(
824                                 pos - '4.5 0 0' * sbar_fontsize_y + '0 1.5 0' * sbar_fontsize_y,
825                                 tm.(teamscores[ts_secondary]),
826                                 4, sbar_fontsize_y * 1, rgb, 1, DRAWFLAG_NORMAL);
827                         
828                         specs = tm.team_size;
829
830                         if(specs < 2)
831                                 specs = 2;
832                         
833                         tmp_x = sbwidth;
834                         tmp_y = 1.25 * sbar_fontsize_y * specs;
835                         drawfill(pos - '1 1 0', tmp + '2 0 0', rgb, 0.2, DRAWFLAG_NORMAL);
836                         
837                         for(pl = players.sort_next; pl; pl = pl.sort_next)
838                         {
839                                 if(pl.team != tm.team)
840                                         continue;
841                                 Sbar_PrintScoreboardItem(pos, pl, (pl.sv_entnum == player_localentnum - 1));
842                                 pos_y += 1.25 * sbar_fontsize_y;
843                                 tmp_y -= 1.25 * sbar_fontsize_y;
844                         }
845                         pos_y += tmp_y + 1.5 * sbar_fontsize_y;
846                 }
847                 // rgb := tempvector :)
848                 rgb = pos + '0 1.5 0' * sbar_fontsize_y;
849                 pos_y += 3 * sbar_fontsize_y;
850                 specs = 0;
851                 for(pl = players.sort_next; pl; pl = pl.sort_next)
852                 {
853                         if(pl.team != COLOR_SPECTATOR)
854                                 continue;
855                         //drawcolorcodedstring(pos, getplayerkey(pl.sb_player, "name"), '8 8 0', 1, 0);
856                         Sbar_PrintScoreboardItem(pos, pl, (pl.sv_entnum == player_localentnum - 1));
857                         pos += '0 1.25 0' * sbar_fontsize_y;
858                         ++specs;
859                 }
860                         
861                 if(specs)
862                         drawstring(rgb, "Spectators", sbar_fontsize, '1 1 1', 1, 0);
863         } else {
864                 pos_x = xmin;
865                 for(pl = players.sort_next; pl; pl = pl.sort_next)
866                 {
867                         if(pl.team == COLOR_SPECTATOR)
868                                 continue;
869                         Sbar_PrintScoreboardItem(pos, pl, (pl.sv_entnum == player_localentnum - 1));
870                         pos_y += 1.25 * sbar_fontsize_y;
871                         tmp_y -= 1.25 * sbar_fontsize_y;
872                 }
873
874                 // rgb := tempvector :)
875                 rgb = pos + '0 1.5 0' * sbar_fontsize_y;
876                 pos_y += 3 * sbar_fontsize_y;
877                 specs = 0;
878                 for(pl = players.sort_next; pl; pl = pl.sort_next)
879                 {
880                         if(pl.team != COLOR_SPECTATOR)
881                                 continue;
882                         Sbar_PrintScoreboardItem(pos, pl, (pl.sv_entnum == player_localentnum - 1));
883                         pos += '0 1.25 0' * sbar_fontsize_y;
884                         ++specs;
885                 }
886                         
887                 if(specs)
888                         drawstring(rgb, "Spectators", sbar_fontsize, '1 1 1', 1, 0);
889         }
890
891         string str;
892         float tl, fl;
893         str = strcat("playing on ^2", shortmapname, "^7");
894         tl = getstatf(STAT_TIMELIMIT);
895         fl = getstatf(STAT_FRAGLIMIT);
896         if(gametype == GAME_LMS)
897         {
898                 if(tl > 0)
899                         str = strcat(str, " for up to ^1", ftos(tl), " minutes^7");
900         }
901         else
902         {
903                 if(tl > 0)
904                         str = strcat(str, " for ^1", ftos(tl), " minutes^7");
905                 if(fl > 0)
906                 {
907                         if(tl > 0)
908                                 str = strcat(str, " or");
909                         str = strcat(str, " until ^3", ScoreString(scores_flags[ps_primary], fl));
910                         if(scores_label[ps_primary] == "score")
911                                 str = strcat(str, " points^7");
912                         else if(scores_label[ps_primary] == "fastest")
913                                 str = strcat(str, " is beaten^7");
914                         else
915                                 str = strcat(str, " ", scores_label[ps_primary]);
916                 }
917         }
918         
919         pos_y += 1.5 * sbar_fontsize_y;
920         drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - sbar_fontsize_x * stringwidth(str, TRUE)), str, sbar_fontsize, 0.8, 0);
921
922         sbar = sbar_save;
923 }
924
925 string MakeRaceString(float cp, float mytime, float histime, float lapdelta, string hisname)
926 {
927         string col;
928         string timestr;
929         string cpname;
930         string lapstr;
931         lapstr = "";
932
933         if(histime == 0) // goal hit
934         {
935                 if(mytime > 0)
936                 {
937                         timestr = strcat("+", ftos_decimals(+mytime, 1));
938                         col = "^1";
939                 }
940                 else if(mytime == 0)
941                 {
942                         timestr = "+0.0";
943                         col = "^3";
944                 }
945                 else
946                 {
947                         timestr = strcat("-", ftos_decimals(-mytime, 1));
948                         col = "^2";
949                 }
950
951                 if(lapdelta > 0)
952                 {
953                         lapstr = strcat(" (-", ftos(lapdelta), "L)");
954                         col = "^2";
955                 }
956                 else if(lapdelta < 0)
957                 {
958                         lapstr = strcat(" (+", ftos(-lapdelta), "L)");
959                         col = "^1";
960                 }
961         }
962         else if(histime > 0) // anticipation
963         {
964                 if(mytime >= histime)
965                         timestr = strcat("+", ftos_decimals(mytime - histime, 1));
966                 else
967                         timestr = mmsss(histime * 10);
968                 col = "^3";
969         }
970         else
971                 col = "^7";
972
973         if(cp)
974                 cpname = strcat("Intermediate ", ftos(cp));
975         else
976                 cpname = "Finish line";
977         
978         if(histime < 0)
979                 return strcat(col, cpname);
980         else if(hisname == "")
981                 return strcat(col, cpname, " (", timestr, ")");
982         else
983                 return strcat(col, cpname, " (", timestr, " ", strcat(hisname, col, lapstr), ")");
984 }
985
986 void dummyfunction(float a1, float a2, float a3, float a4, float a5, float a6, float a7, float a8)
987 {
988 }
989
990 void Sbar_Score(float margin)
991 {
992         float timelimit, timeleft, minutes, seconds, distribution, myplace, score;
993         vector sbar_save, place;
994         entity tm, pl, me;
995         sbar_save = sbar;
996         
997         myteam = GetPlayerColor(player_localentnum - 1);
998
999         sbar_y = vid_conheight - (32+12);
1000         sbar_x -= margin;
1001         
1002         place = '-48 -12 0';
1003         if(teamplay)
1004         {
1005                 // Layout:
1006                 //
1007                 //   team1 team3 team4
1008                 //
1009                 //         TEAM2
1010                 //for(i = 0; i < 4; ++i)
1011                 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1012                 {
1013                         if(tm.team == COLOR_SPECTATOR || !tm.team_size) // no players? don't display
1014                                 continue;
1015                         // -32*4 = -128
1016                         score = tm.(teamscores[ts_primary]);
1017                         if(tm.team == myteam)
1018                                 Sbar_DrawXNum('-128 0 0', score, 4, 32, GetTeamRGB(tm.team), 1, DRAWFLAG_NORMAL);
1019                         else
1020                         {
1021                                 Sbar_DrawXNum(place, score, 4, 12, GetTeamRGB(tm.team), 1, DRAWFLAG_NORMAL);
1022                                 place_x -= 4*12;
1023                         }
1024                 }
1025         } else {
1026                 // me vector := [team/connected frags id]
1027                 myplace = 0;
1028                 for(me = players.sort_next; me; me = me.sort_next)
1029                 {
1030                         if(me.team != COLOR_SPECTATOR)
1031                                 ++myplace;
1032                         if(me.sv_entnum == player_localentnum - 1)
1033                                 break;
1034                 }
1035                 pl = players;
1036                 if(pl == me)
1037                         pl = pl.sort_next;
1038                 
1039                 if(pl) {
1040                         distribution = me.(scores[ps_primary]) - pl.(scores[ps_primary]);
1041                 } else
1042                         distribution = 0;
1043                 
1044                 if(myplace == 1)
1045                         Sbar_DrawXNum('-36 -12 0', myplace, 3, 12, '1 1 1', 1, DRAWFLAG_NORMAL);
1046                 else if(myplace == 2)
1047                         Sbar_DrawXNum('-36 -12 0', myplace, 3, 12, '1 1 0', 1, DRAWFLAG_NORMAL);
1048                 else
1049                         Sbar_DrawXNum('-36 -12 0', myplace, 3, 12, '1 0 0', 1, DRAWFLAG_NORMAL);
1050
1051                 score = me.(scores[ps_primary]);
1052                 if(distribution >= 0)
1053                 {
1054                         Sbar_DrawXNum('-84 -12 0', distribution, 4, 12, ' 1 1 1', 1, DRAWFLAG_NORMAL);
1055                         Sbar_DrawXNum('-128 0 0', score, 4, 32, '1 1 1', 1, DRAWFLAG_NORMAL);
1056                 } else if(distribution >= -5)
1057                 {
1058                         Sbar_DrawXNum('-84 -12 0', distribution, 4, 12, ' 1 1 0', 1, DRAWFLAG_NORMAL);
1059                         Sbar_DrawXNum('-128 0 0', score, 4, 32, '1 1 0', 1, DRAWFLAG_NORMAL);
1060                 } else {
1061                         Sbar_DrawXNum('-84 -12 0', distribution, 4, 12, ' 1 0 0', 1, DRAWFLAG_NORMAL);
1062                         Sbar_DrawXNum('-128 0 0', score, 4, 32, '1 0 0', 1, DRAWFLAG_NORMAL);
1063                 }
1064         }
1065         timelimit = getstatf(STAT_TIMELIMIT);
1066         if(timelimit)
1067         {
1068                 timeleft = max(0, timelimit * 60 - time);
1069                 minutes = floor(timeleft / 60);
1070                 seconds = floor(timeleft - minutes*60);
1071                 if(minutes >= 5)
1072                 {
1073                         Sbar_DrawXNum('-72 32 0', minutes, 3, 12, '1 1 1', 1, DRAWFLAG_NORMAL);
1074                         drawpic(sbar + '-36 32 0', "gfx/num_colon", '12 12 0', '1 1 1', sbar_alpha_fg, 0);
1075                         Sbar_DrawXNum('-24 32 0', seconds, -2, 12, '1 1 1', 1, DRAWFLAG_NORMAL);
1076                 } else if(minutes >= 1)
1077                 {
1078                         Sbar_DrawXNum('-72 32 0', minutes, 3, 12, '1 1 0', 1, DRAWFLAG_NORMAL);
1079                         drawpic(sbar + '-36 32 0', "gfx/num_colon", '12 12 0', '1 1 0', sbar_alpha_fg, 0);
1080                         Sbar_DrawXNum('-24 32 0', seconds, -2, 12, '1 1 0', 1, DRAWFLAG_NORMAL);
1081                 } else {
1082                         Sbar_DrawXNum('-24 32 0', seconds, -2, 12, '1 0 0', 1, DRAWFLAG_NORMAL);
1083                 }
1084         } else {
1085                 minutes = floor(time / 60);
1086                 seconds = floor(time - minutes*60);
1087                 Sbar_DrawXNum('-72 32 0', minutes, 3, 12, '1 1 1', 1, DRAWFLAG_NORMAL);
1088                 drawpic(sbar + '-36 32 0', "gfx/num_colon", '12 12 0', '1 1 1', sbar_alpha_fg, 0);
1089                 Sbar_DrawXNum('-24 32 0', seconds, -2, 12, '1 1 1', 1, DRAWFLAG_NORMAL);
1090         }
1091
1092         if(gametype == GAME_RACE)
1093         {
1094                 drawfont = sbar_bigfont;
1095                 float a;
1096                 vector m;
1097                 string s, forcetime;
1098
1099                 m = '0.5 0 0' * vid_conwidth + '0 0.25 0' * vid_conheight;
1100
1101                 if(race_checkpointtime)
1102                 {
1103
1104                         a = bound(0, 2 - (time - race_checkpointtime), 1);
1105                         s = "";
1106                         forcetime = "";
1107                         if(a > 0) // just hit a checkpoint?
1108                         {
1109                                 if(race_time && race_previousbesttime)
1110                                         s = MakeRaceString(race_checkpoint, race_time / 10 - race_previousbesttime / 10, 0, 0, race_previousbestname);
1111                                 else
1112                                         s = MakeRaceString(race_checkpoint, 0, -1, 0, race_previousbestname);
1113                                 if(race_time)
1114                                         forcetime = mmsss(race_time);
1115                         }
1116                         else
1117                         {
1118                                 if(race_laptime && race_nextbesttime)
1119                                 {
1120                                         a = bound(0, 2 - ((race_laptime + race_nextbesttime/10) - time), 1);
1121                                         if(a > 0) // next one?
1122                                         {
1123                                                 s = MakeRaceString(race_nextcheckpoint, time - race_laptime, race_nextbesttime / 10, 0, race_nextbestname);
1124                                         }
1125                                 }
1126                         }
1127
1128                         if(s != "" && a > 0)
1129                         {
1130                                 dummyfunction(0, 0, 0, 0, 0, 0, 0, 0); // work around DP bug (set OFS_PARAM5 to 0)
1131                                 drawcolorcodedstring(m - '0 16 0' - '8 0 0' * stringwidth(s, TRUE), s, '16 16 0', sbar_alpha_fg * a, 0);
1132                         }
1133
1134                         if(forcetime != "")
1135                         {
1136                                 float sz;
1137                                 a = bound(0, 1 - 2 * (time - race_checkpointtime), 1);
1138                                 sz = 1.2 / (a + 0.2);
1139                                 drawstring(m - '0 0 0' - '0 16 0' * (sz - 1) - '16 0 0' * sz * stringwidth(forcetime, FALSE), forcetime, '32 32 0' * sz, '1 1 1', sbar_alpha_fg * a, 0);
1140                                 a = 1 - a;
1141                         }
1142                         else
1143                                 a = 1;
1144
1145                         if(race_laptime)
1146                         {
1147                                 s = mmsss(10*(time - race_laptime));
1148                                 drawstring(m - '0 0 0' - '16 0 0' * stringwidth(s, FALSE), s, '32 32 0', '1 1 1', sbar_alpha_fg * a, 0);
1149                         }
1150                 }
1151                 else
1152                 {
1153                         if(race_mycheckpointtime)
1154                         {
1155                                 a = bound(0, 2 - (time - race_mycheckpointtime), 1);
1156                                 s = MakeRaceString(race_mycheckpoint, race_mycheckpointdelta / 10, -!race_mycheckpointenemy, race_mycheckpointlapsdelta, race_mycheckpointenemy);
1157                                 dummyfunction(0, 0, 0, 0, 0, 0, 0, 0); // work around DP bug (set OFS_PARAM5 to 0)
1158                                 drawcolorcodedstring(m - '0 16 0' - '8 0 0' * stringwidth(s, TRUE), s, '16 16 0', sbar_alpha_fg * a, 0);
1159                         }
1160                         if(race_othercheckpointtime && race_othercheckpointenemy != "")
1161                         {
1162                                 a = bound(0, 2 - (time - race_othercheckpointtime), 1);
1163                                 s = MakeRaceString(race_othercheckpoint, -race_othercheckpointdelta / 10, -!race_othercheckpointenemy, race_othercheckpointlapsdelta, race_othercheckpointenemy);
1164                                 dummyfunction(0, 0, 0, 0, 0, 0, 0, 0); // work around DP bug (set OFS_PARAM5 to 0)
1165                                 drawcolorcodedstring(m - '0 0 0' - '8 0 0' * stringwidth(s, TRUE), s, '16 16 0', sbar_alpha_fg * a, 0);
1166                         }
1167                 }
1168
1169                 drawfont = sbar_font;
1170         }
1171
1172         sbar = sbar_save;
1173 }
1174
1175 void Sbar_MiniscoreItem(vector pos, entity pl, float is_self)
1176 {
1177         float x, score;
1178         pos_x += 72;
1179         
1180         if(teamplay)
1181                 drawfill(pos + '0 1 0', '40 6 0', GetTeamRGB(pl.team)*0.5, 1, DRAWFLAG_NORMAL);
1182         else
1183                 drawfill(pos + '0 1 0', '40 6 0', '0.5 0.5 0.5', 0.5, DRAWFLAG_NORMAL);
1184         x = pos_x;
1185         pos_x += 5*8;
1186         score = pl.(scores[ps_primary]);
1187         pos_x -= stringwidth(ftos(score), FALSE)*8;
1188         drawstring(pos, ftos(score), '8 8 0', '1 1 1', 1, DRAWFLAG_NORMAL);
1189         pos_x = x;
1190         if(is_self)
1191         {
1192                 pos_x += 48;
1193                 drawstring(pos, "\x0D", '8 8 0', '1 1 1', 1, DRAWFLAG_NORMAL);
1194                 pos_x += 8;
1195         } else
1196                 pos_x += 56;
1197         drawcolorcodedstring(pos, getplayerkey(pl.sv_entnum, "name"), '8 8 0', 1, 0);
1198 }
1199
1200 void Sbar_MiniscoreTeamItem(vector pos, float color, float frags, float is_self)
1201 {
1202         float x;
1203         pos_x += 72;
1204         
1205         if(teamplay)
1206                 drawfill(pos + '0 1 0', '40 6 0', GetTeamRGB(color)*0.5, 1, DRAWFLAG_NORMAL);
1207         else
1208                 drawfill(pos + '0 1 0', '40 6 0', '0.5 0.5 0.5', 0.5, DRAWFLAG_NORMAL);
1209         x = pos_x;
1210         pos_x += 5*8;
1211         pos_x -= stringwidth(ftos(frags), FALSE)*8;
1212         drawstring(pos, ftos(frags), '8 8 0', '1 1 1', 1, DRAWFLAG_NORMAL);
1213         pos_x = x;
1214         if(is_self)
1215         {
1216                 pos_x += 48;
1217                 drawstring(pos, "\x0D", '8 8 0', '1 1 1', 1, DRAWFLAG_NORMAL);
1218                 pos_x += 8;
1219         } else
1220                 pos_x += 56;
1221         drawstring(pos, GetTeamName(color), '8 8 0', '1 1 1', 1, DRAWFLAG_NORMAL);
1222 }
1223
1224 void Sbar_MiniDeathmatchOverlay(vector pos)
1225 {
1226         float numlines, up, down, score;
1227         entity me, tm, pl;
1228         float miniscoreboard_size;
1229         miniscoreboard_size = cvar("sbar_miniscoreboard_size");
1230         
1231         if(miniscoreboard_size == 0)
1232                 return;
1233         pos_y = vid_conheight - 8;
1234         
1235         if(miniscoreboard_size < 0)
1236                 numlines = (vid_conheight - sbar_y + 7) / 8;
1237         else
1238                 numlines = miniscoreboard_size;
1239
1240         // give up if there isn't enough room
1241         if(pos_x >= vid_conwidth || pos_y >= vid_conheight || numlines < 1)
1242                 return;
1243
1244         // me vector := [team/connected frags id]
1245         for(me = players.sort_next; me; me = me.sort_next)
1246         {
1247                 if(me.sv_entnum == player_localentnum - 1)
1248                         break;
1249         }
1250
1251         if(teamplay)
1252                 numlines -= numteams;
1253
1254         // figure out how many players above and below we can show
1255         up = floor(numlines/2);
1256         down = up;
1257         if((up + down) > numlines)
1258                 down = numlines - up;
1259
1260         // render bottom-up
1261         for(pl = me.sort_next; pl && down > 0; pl = pl.sort_next)
1262         {
1263                 if(pl.team == COLOR_SPECTATOR)
1264                         continue;
1265                 Sbar_MiniscoreItem(pos, pl, false);
1266                 pos_y -= 9;
1267                 --down;
1268         }
1269         Sbar_MiniscoreItem(pos, me, true);
1270         pos_y -= 9;
1271         up += down; // if there weren't enough lines below... add them
1272         for(pl = me.sort_prev; pl && up > 0; pl = pl.sort_prev)
1273         {
1274                 if(pl.team == COLOR_SPECTATOR)
1275                         continue;
1276                 Sbar_MiniscoreItem(pos, pl, false);
1277                 pos_y -= 9;
1278                 --up;
1279         }
1280
1281         if(teamplay)
1282         {
1283                 for(tm = teams.sort_next; tm.sort_next; tm = tm.sort_next);
1284                 for(; tm; tm = tm.sort_prev)
1285                 {
1286                         if(!tm.team_size || tm.team == COLOR_SPECTATOR)
1287                                 continue;
1288                         score = tm.(teamscores[ts_primary]);
1289                         Sbar_MiniscoreTeamItem(pos, tm.team, score, (tm.team == me.team));
1290                         pos_y -= 9;
1291                 }
1292         }
1293 }
1294
1295 float Sbar_WouldDrawScoreboard ()
1296 {
1297         if (sb_showscores)
1298                 return 1;
1299         else if (intermission == 1)
1300                 return 1;
1301         else if (intermission == 2)
1302                 return 1;
1303         else if (getstati(STAT_HEALTH) <= 0 && cvar("cl_deathscoreboard"))
1304                 return 1;
1305         else if(sb_showscores_force)
1306                 return 1;
1307         return 0;
1308 }
1309
1310 void Sbar_Draw (void)
1311 {
1312         float i;
1313         float x, fade;
1314         float stat_items, stat_weapons;
1315
1316         sbar_fontsize = Sbar_GetFontsize();
1317
1318         if(spectatee_status)
1319         {
1320                 string s;
1321                 vector o;
1322                 o = '1 0 0' * vid_conwidth;
1323                 if(spectatee_status == -1)
1324                         s = "^1Observing";
1325                 else
1326                         s = strcat("^1Spectating ^7", getplayerkey(spectatee_status - 1, "name"));
1327                 dummyfunction(0, 0, 0, 0, 0, 0, 0, 0); // work around DP bug (set OFS_PARAM5 to 0)
1328                 drawcolorcodedstring(
1329                         o - sbar_fontsize_x * '1 0 0' * stringwidth(s, TRUE),
1330                         s,
1331                         sbar_fontsize,
1332                         sbar_alpha_fg,
1333                         0
1334                 );
1335                 o += sbar_fontsize_y * '0 1 0';
1336
1337                 if(spectatee_status == -1)
1338                         s = "^1Press ^3primary fire^1 to spectate";
1339                 else
1340                         s = "^1Press ^3primary fire^1 for another player";
1341                 dummyfunction(0, 0, 0, 0, 0, 0, 0, 0); // work around DP bug (set OFS_PARAM5 to 0)
1342                 drawcolorcodedstring(
1343                         o - sbar_fontsize_x * '1 0 0' * stringwidth(s, TRUE),
1344                         s,
1345                         sbar_fontsize,
1346                         sbar_alpha_fg,
1347                         0
1348                 );
1349                 o += sbar_fontsize_y * '0 1 0';
1350
1351                 if(spectatee_status == -1)
1352                         s = "^1Use ^3weapon switching^1 to change the speed";
1353                 else
1354                         s = "^1Press ^3secondary fire^1 to observe";
1355                 dummyfunction(0, 0, 0, 0, 0, 0, 0, 0); // work around DP bug (set OFS_PARAM5 to 0)
1356                 drawcolorcodedstring(
1357                         o - sbar_fontsize_x * '1 0 0' * stringwidth(s, TRUE),
1358                         s,
1359                         sbar_fontsize,
1360                         sbar_alpha_fg,
1361                         0
1362                 );
1363                 o += sbar_fontsize_y * '0 1 0';
1364
1365                 if(gametype == GAME_ARENA)
1366                         s = "^1Wait for your turn to join";
1367                 else if(gametype == GAME_LMS)
1368                 {
1369                         entity sk;
1370                         sk = playerslots[player_localentnum - 1];
1371                         if(sk.(scores[ps_primary]) >= 666)
1372                                 s = "^1Match has already begun";
1373                         else if(sk.(scores[ps_primary]) > 0)
1374                                 s = "^1You have no more lives left";
1375                         else
1376                                 s = "^1Press ^7jump^1 to join";
1377                 }
1378                 else
1379                         s = "^1Press ^7jump^1 to join";
1380                 dummyfunction(0, 0, 0, 0, 0, 0, 0, 0); // work around DP bug (set OFS_PARAM5 to 0)
1381                 drawcolorcodedstring(
1382                         o - sbar_fontsize_x * '1 0 0' * stringwidth(s, TRUE),
1383                         s,
1384                         sbar_fontsize,
1385                         sbar_alpha_fg,
1386                         0
1387                 );
1388                 o += sbar_fontsize_y * '0 1 0';
1389         }
1390         
1391         //Sbar_SortFrags();
1392         Sbar_UpdatePlayerTeams();
1393
1394         sb_lines = 24;
1395
1396         if (sb_showscores)
1397                 Sbar_DrawScoreboard();
1398         else if (intermission == 1)
1399         {
1400                 Sbar_DrawScoreboard();
1401                 return;
1402         }
1403         else if (intermission == 2)
1404                 Sbar_FinaleOverlay();
1405         else
1406         {
1407                 if (sb_showscores || sb_showscores_force || (getstati(STAT_HEALTH) <= 0 && cvar("cl_deathscoreboard")))
1408                 {
1409                         sbar_x = (vid_conwidth - 640.0)*0.5;
1410                         sbar_y = vid_conheight - 47;
1411                         //Sbar_DrawAlphaPic (sbar_x, sbar_y, sb_scorebar, sbar_alpha_bg.value);
1412                         //drawpic('0 0 0', "gfx/scorebar", '0 0 0', '1 1 1', cvar("sbar_alpha_bg"), 0);
1413                         Sbar_DrawScoreboard ();
1414                 }
1415                 else
1416                 {
1417                         if (sb_lines && sbar_hudselector == 1)
1418                         {
1419                                 stat_items = getstati(STAT_ITEMS);
1420                                 stat_weapons = getstati(STAT_WEAPONS);
1421
1422                                 sbar_x = (vid_conwidth - 320.0)*0.5;
1423                                 sbar_y = vid_conheight - 24.0 - 16.0;
1424                                 sbar_z = 0;
1425                         
1426                                 fade = 3.2 - 2 * (time - weapontime);
1427                                 fade = bound(0.7, fade, 1);
1428
1429                                 x = 1.0;
1430                                 Sbar_DrawWeapon_Clear();
1431                                 for(i = 1; i <= 24; ++i)
1432                                 {
1433                                         if(weaponimpulse[i-1] >= 0)
1434                                         if(stat_weapons & x)
1435                                         {
1436                                                 Sbar_DrawWeapon(i-1, fade, (i == activeweapon));
1437                                         }
1438                                         x *= 2;
1439                                 }
1440
1441                                 // armor
1442                                 x = getstati(STAT_ARMOR);
1443                                 if (x > 0)
1444                                 {
1445                                         // "gfx/sb_armor"
1446                                         //Sbar_DrawStretchPic (72, 0, sb_armor[0], sbar_alpha_fg.value, 24, 24);
1447                                         drawpic(sbar + '72 0 0', "gfx/sb_armor", '24 24 0', '1 1 1', sbar_alpha_fg, 0);
1448                                         if(x > 200)
1449                                                 Sbar_DrawXNum('0 0 0', x, 3, 24, '0 1 0', 1, 0);
1450                                         else if(x > 100)
1451                                                 Sbar_DrawXNum('0 0 0', x, 3, 24, '0.2 1 0', 1, 0);
1452                                         else if(x > 50)
1453                                                 Sbar_DrawXNum('0 0 0', x, 3, 24, '0.6 0.7 0.8', 1, 0);
1454                                         else if(x > 25)
1455                                                 Sbar_DrawXNum('0 0 0', x, 3, 24, '1 1 0.2', 1, 0);
1456                                         else
1457                                                 Sbar_DrawXNum('0 0 0', x, 3, 24, '0.7 0 0', 1, 0);
1458                                 }
1459
1460                                 // health
1461                                 x = getstati(STAT_HEALTH);
1462                                 if (x != 0)
1463                                 {
1464                                         // "gfx/sb_health"
1465                                         //Sbar_DrawStretchPic (184, 0, sb_health, sbar_alpha_fg.value, 24, 24);
1466                                         drawpic(sbar + '184 0 0', "gfx/sb_health", '24 24 0', '1 1 1', sbar_alpha_fg, 0);
1467                                         if(x > 200)
1468                                                 Sbar_DrawXNum('112 0 0', x, 3, 24, '0 1 0', 1, 0);
1469                                         else if(x > 100)
1470                                                 Sbar_DrawXNum('112 0 0', x, 3, 24, '0.2 1 0', 1, 0);
1471                                         else if(x > 50)
1472                                                 Sbar_DrawXNum('112 0 0', x, 3, 24, '0.6 0.7 0.8', 1, 0);
1473                                         else if(x > 25)
1474                                                 Sbar_DrawXNum('112 0 0', x, 3, 24, '1 1 0.2', 1, 0);
1475                                         else
1476                                                 Sbar_DrawXNum('112 0 0', x, 3, 24, '0.7 0 0', 1, 0);
1477                                 }
1478
1479                                 // ammo
1480                                 x = getstati(STAT_AMMO);
1481                                 if ((stat_items & IT_AMMO) || x != 0)
1482                                 {
1483                                         if (stat_items & IT_SHELLS)
1484                                                 drawpic(sbar + '296 0 0', "gfx/sb_shells", '24 24 0', '1 1 1', sbar_alpha_fg, 0);
1485                                         else if (stat_items & IT_NAILS)
1486                                                 drawpic(sbar + '296 0 0', "gfx/sb_bullets", '24 24 0', '1 1 1', sbar_alpha_fg, 0);
1487                                         else if (stat_items & IT_ROCKETS)
1488                                                 drawpic(sbar + '296 0 0', "gfx/sb_rocket", '24 24 0', '1 1 1', sbar_alpha_fg, 0);
1489                                         else if (stat_items & IT_CELLS)
1490                                                 drawpic(sbar + '296 0 0', "gfx/sb_cells", '24 24 0', '1 1 1', sbar_alpha_fg, 0);
1491                                         if(x > 10)
1492                                                 Sbar_DrawXNum('224 0 0', x, 3, 24, '0.6 0.7 0.8', 1, 0);
1493                                         else
1494                                                 Sbar_DrawXNum('224 0 0', x, 3, 24, '0.7 0 0', 1, 0);
1495                                 }
1496
1497                                 if (sbar_x + 320 + 160 <= vid_conwidth)
1498                                         Sbar_MiniDeathmatchOverlay(sbar + '320 0 0');
1499                                 if (sbar_x > 0)
1500                                         Sbar_Score(16);
1501                                 // The margin can be at most 8 to support 640x480 console size:
1502                                 //   320 + 2 * (144 + 16) = 640
1503                         }
1504                         else if (sb_lines)
1505                         {
1506                         
1507                                 stat_items = getstati(STAT_ITEMS);
1508                                 stat_weapons = getstati(STAT_ITEMS);
1509                         
1510                                 sbar_x = (vid_conwidth - 640.0)*0.5;
1511                                 sbar_y = vid_conheight - 47;
1512                                 sbar_z = 0;
1513
1514                                 fade = 3 - 2 * (time - weapontime);
1515
1516                                 x = 1.0;
1517                                 Sbar_DrawWeapon_Clear();
1518                                 for(i = WEP_FIRST; i <= WEP_LAST; ++i)
1519                                 {
1520                                         if(weaponimpulse[i-1] >= 0)
1521                                         if(stat_weapons & x)
1522                                         {
1523                                                 Sbar_DrawWeapon(i-1, fade, (i == activeweapon));
1524                                         }
1525                                         x *= 2;
1526                                 }
1527
1528                                 if (sb_lines > 24)
1529                                         drawpic(sbar, "gfx/sbar", '0 0 0', '1 1 1', sbar_alpha_fg, 0);
1530                                 else
1531                                         drawpic(sbar, "gfx/sbar_minimal", '0 0 0', '1 1 1', sbar_alpha_fg, 0);
1532
1533                                 // armor
1534                                 // (340-3*24) = 268
1535                                 Sbar_DrawXNum('268 12 0', getstati(STAT_ARMOR), 3, 24, '0.6 0.7 0.8', 1, 0);
1536
1537                                 // health
1538                                 // (154-3*24) = 82
1539                                 x = getstati(STAT_HEALTH);
1540                                 if(x > 100)
1541                                         Sbar_DrawXNum('82 12 0', x, 3, 24, '1 1 1', 1, 0);
1542                                 else if(x <= 25 && time - floor(time) > 0.5)
1543                                         Sbar_DrawXNum('82 12 0', x, 3, 24, '0.7 0 0', 1, 0);
1544                                 else
1545                                         Sbar_DrawXNum('81 12 0', x, 3, 24, '0.6 0.7 0.8', 1, 0);
1546
1547                                 // AK dont draw ammo for the laser
1548                                 x = getstati(STAT_AMMO);
1549                                 if(activeweapon != 12)
1550                                 {
1551                                         // (519-3*24) = 447
1552                                         if (stat_items & IT_SHELLS)
1553                                                 drawpic(sbar + '519 0 0', "gfx/sb_shells", '0 0 0', '1 1 1', sbar_alpha_fg, 0);
1554                                         else if (stat_items & IT_NAILS)
1555                                                 drawpic(sbar + '519 0 0', "gfx/sb_bullets", '0 0 0', '1 1 1', sbar_alpha_fg, 0);
1556                                         else if (stat_items & IT_ROCKETS)
1557                                                 drawpic(sbar + '519 0 0', "gfx/sb_rocket", '0 0 0', '1 1 1', sbar_alpha_fg, 0);
1558                                         else if (stat_items & IT_CELLS)
1559                                                 drawpic(sbar + '519 0 0', "gfx/sb_cells", '0 0 0', '1 1 1', sbar_alpha_fg, 0);
1560                                         if(x > 10)
1561                                                 Sbar_DrawXNum('447 12 0', x, 3, 24, '0.6 0.7 0.8', 1, 0);
1562                                         else
1563                                                 Sbar_DrawXNum('447 12 0', x, 3, 24, '0.7 0 0', 1, 0);
1564                                 }
1565
1566                                 if (sb_lines > 24)
1567                                         drawpic(sbar, "gfx/sbar_overlay", '0 0 0', '1 1 1', 1, DRAWFLAG_MODULATE);
1568
1569                                 if (sbar_x + 600 + 160 <= vid_conwidth)
1570                                         Sbar_MiniDeathmatchOverlay (sbar + '600 0 0');
1571
1572                                 if (sbar_x > 0)
1573                                         Sbar_Score(-16);
1574                                 // Because:
1575                                 //   Mini scoreboard uses 12*4 per other team, that is, 144
1576                                 //   pixels when there are four teams...
1577                                 //   Nexuiz by default sets vid_conwidth to 800... makes
1578                                 //   sbar_x == 80...
1579                                 //   so we need to shift it by 64 pixels to the right to fit
1580                                 //   BUT: then it overlaps with the image that gets drawn
1581                                 //   for viewsize 100! Therefore, just account for 3 teams,
1582                                 //   that is, 96 pixels mini scoreboard size, needing 16 pixels
1583                                 //   to the right!
1584                         }
1585                 
1586                 
1587                         if(gametype == GAME_KEYHUNT)
1588                         {
1589                                 CSQC_kh_hud();
1590                         } else if(gametype == GAME_CTF)
1591                         {
1592                                 CSQC_ctf_hud();
1593                         }
1594                 }
1595         }
1596 }
1597
1598 void CSQC_ctf_hud(void)
1599 {
1600         // cvar("sbar_flagstatus_right") move the flag icons right
1601         // cvar("sbar_flagstatus_pos") pixel position of the nexuiz flagstatus icons
1602         float redflag, blueflag;
1603         float stat_items;
1604         vector pos;
1605         
1606         stat_items = getstati(STAT_ITEMS);
1607         redflag = (stat_items/32768) & 3;
1608         blueflag = (stat_items/131072) & 3;
1609
1610         /**
1611          * FTEQCC BUG!
1612          * For some reason now not even THAT works there...
1613          * Maybe the minus' precedence screws it up? The last one there, maybe I should use brackets
1614          **
1615          * pos_x = (cvar("sbar_flagstatus_right")) ? vid_conwidth - 10 - sbar_x - 64 : 10 - sbar_x;
1616          ** Should try those later:
1617          * pos_x = (cvar("sbar_flagstatus_right")) ? (vid_conwidth - 10 - sbar_x - 64) : (10 - sbar_x);
1618          * pos_x = ( (cvar("sbar_flagstatus_right")) ? vid_conwidth - 10 - 64 : 10 ) - sbar_x;
1619          */
1620         
1621         if(cvar("sbar_flagstatus_right"))
1622                 pos_x = vid_conwidth - 10 - sbar_x - 64;
1623         else
1624                 pos_x = 10 - sbar_x;
1625         
1626         pos_z = 0;
1627
1628         if(sbar_hudselector == 1)
1629                 pos_y = (vid_conheight - sbar_y) - cvar("sbar_flagstatus_pos") - 64;
1630         else
1631                 pos_y = -117;
1632
1633         pos += sbar;
1634
1635         switch(redflag)
1636         {
1637         case 1: drawpic(pos, "gfx/sb_flag_red_taken", '0 0 0', '1 1 1', 1, DRAWFLAG_NORMAL); break;
1638         case 2: drawpic(pos, "gfx/sb_flag_red_lost", '0 0 0', '1 1 1', 1, DRAWFLAG_NORMAL); break;
1639         case 3: drawpic(pos, "gfx/sb_flag_red_carrying", '0 0 0', '1 1 1', 1, DRAWFLAG_NORMAL); break;
1640         }
1641
1642         pos_y -= 64;
1643         
1644         switch(blueflag)
1645         {
1646         case 1: drawpic(pos, "gfx/sb_flag_blue_taken", '0 0 0', '1 1 1', 1, 0); break;
1647         case 2: drawpic(pos, "gfx/sb_flag_blue_lost", '0 0 0', '1 1 1', 1, 0); break;
1648         case 3: drawpic(pos, "gfx/sb_flag_blue_carrying", '0 0 0', '1 1 1', 1, 0); break;
1649         }
1650 }