]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/client/hud/panel/modicons.qc
Don't show an empty race panel in modes where times don't matter, also add a setting...
[xonotic/xonotic-data.pk3dir.git] / qcsrc / client / hud / panel / modicons.qc
1 #include "modicons.qh"
2
3 #include <common/mapinfo.qh>
4 #include <common/ent_cs.qh>
5 #include <server/mutators/mutator/gamemode_ctf.qh> // TODO: remove
6
7 // Mod icons (#10)
8
9 bool mod_active; // is there any active mod icon?
10
11 void DrawCAItem(vector myPos, vector mySize, float aspect_ratio, int layout, int i)
12 {
13     TC(int, layout); TC(int, i);
14         int stat = -1;
15         string pic = "";
16         vector color = '0 0 0';
17         switch(i)
18         {
19                 case 0: stat = STAT(REDALIVE); pic = "player_red"; color = '1 0 0'; break;
20                 case 1: stat = STAT(BLUEALIVE); pic = "player_blue"; color = '0 0 1'; break;
21                 case 2: stat = STAT(YELLOWALIVE); pic = "player_yellow"; color = '1 1 0'; break;
22                 default:
23                 case 3: stat = STAT(PINKALIVE); pic = "player_pink"; color = '1 0 1'; break;
24         }
25
26         if(mySize.x/mySize.y > aspect_ratio)
27         {
28                 i = aspect_ratio * mySize.y;
29                 myPos.x = myPos.x + (mySize.x - i) / 2;
30                 mySize.x = i;
31         }
32         else
33         {
34                 i = 1/aspect_ratio * mySize.x;
35                 myPos.y = myPos.y + (mySize.y - i) / 2;
36                 mySize.y = i;
37         }
38
39         if(layout)
40         {
41                 drawpic_aspect_skin(myPos, pic, vec2(0.7 * mySize.x, mySize.y), '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
42                 drawstring_aspect(myPos + eX * 0.7 * mySize.x, ftos(stat), vec2(0.3 * mySize.x, mySize.y), color, panel_fg_alpha, DRAWFLAG_NORMAL);
43         }
44         else
45                 drawstring_aspect(myPos, ftos(stat), mySize, color, panel_fg_alpha, DRAWFLAG_NORMAL);
46 }
47
48 // Clan Arena and Freeze Tag HUD modicons
49 void HUD_Mod_CA(vector myPos, vector mySize)
50 {
51         mod_active = 1; // required in each mod function that always shows something
52
53         int layout;
54         if(gametype == MAPINFO_TYPE_CA)
55                 layout = autocvar_hud_panel_modicons_ca_layout;
56         else //if(gametype == MAPINFO_TYPE_FREEZETAG)
57                 layout = autocvar_hud_panel_modicons_freezetag_layout;
58         int rows, columns;
59         float aspect_ratio;
60         aspect_ratio = (layout) ? 2 : 1;
61         rows = HUD_GetRowCount(team_count, mySize, aspect_ratio);
62         columns = ceil(team_count/rows);
63
64         int i;
65         float row = 0, column = 0;
66         vector pos = '0 0 0', itemSize;
67         itemSize = vec2(mySize.x / columns, mySize.y / rows);
68         for(i=0; i<team_count; ++i)
69         {
70                 pos.x = myPos.x + column * itemSize.x;
71                 pos.y = myPos.y + row * itemSize.y;
72
73                 DrawCAItem(pos, itemSize, aspect_ratio, layout, i);
74
75                 ++row;
76                 if(row >= rows)
77                 {
78                         row = 0;
79                         ++column;
80                 }
81         }
82 }
83
84 // CTF HUD modicon section
85 int redflag_prevframe, blueflag_prevframe, yellowflag_prevframe, pinkflag_prevframe, neutralflag_prevframe; // status during previous frame
86 int redflag_prevstatus, blueflag_prevstatus, yellowflag_prevstatus, pinkflag_prevstatus, neutralflag_prevstatus; // last remembered status
87 float redflag_statuschange_time, blueflag_statuschange_time, yellowflag_statuschange_time, pinkflag_statuschange_time, neutralflag_statuschange_time; // time when the status changed
88
89 void HUD_Mod_CTF_Reset()
90 {
91         redflag_prevstatus = blueflag_prevstatus = yellowflag_prevstatus = pinkflag_prevstatus = neutralflag_prevstatus = 0;
92         redflag_prevframe = blueflag_prevframe = yellowflag_prevframe = pinkflag_prevframe = neutralflag_prevframe = 0;
93         redflag_statuschange_time = blueflag_statuschange_time = yellowflag_statuschange_time = pinkflag_statuschange_time = neutralflag_statuschange_time = 0;
94 }
95
96 int autocvar__teams_available;
97 void HUD_Mod_CTF(vector pos, vector mySize)
98 {
99         vector redflag_pos, blueflag_pos, yellowflag_pos, pinkflag_pos, neutralflag_pos;
100         vector flag_size;
101         float f; // every function should have that
102
103         int redflag, blueflag, yellowflag, pinkflag, neutralflag; // current status
104         float redflag_statuschange_elapsedtime = 0, blueflag_statuschange_elapsedtime = 0, yellowflag_statuschange_elapsedtime = 0, pinkflag_statuschange_elapsedtime = 0, neutralflag_statuschange_elapsedtime = 0; // time since the status changed
105         bool ctf_oneflag; // one-flag CTF mode enabled/disabled
106         bool ctf_stalemate; // currently in stalemate
107         int stat_items = STAT(CTF_FLAGSTATUS);
108         float fs, fs2, fs3, size1, size2;
109         vector e1, e2;
110
111         int nteams = autocvar__teams_available;
112
113         redflag = (stat_items/CTF_RED_FLAG_TAKEN) & 3;
114         blueflag = (stat_items/CTF_BLUE_FLAG_TAKEN) & 3;
115         yellowflag = (stat_items/CTF_YELLOW_FLAG_TAKEN) & 3;
116         pinkflag = (stat_items/CTF_PINK_FLAG_TAKEN) & 3;
117         neutralflag = (stat_items/CTF_NEUTRAL_FLAG_TAKEN) & 3;
118
119         ctf_oneflag = (stat_items & CTF_FLAG_NEUTRAL);
120
121         ctf_stalemate = (stat_items & CTF_STALEMATE);
122
123         mod_active = (redflag || blueflag || yellowflag || pinkflag || neutralflag || (stat_items & CTF_SHIELDED));
124
125         if (autocvar__hud_configure) {
126                 redflag = 1;
127                 blueflag = 2;
128                 if (nteams & BIT(2))
129                         yellowflag = 2;
130                 if (nteams & BIT(3))
131                         pinkflag = 3;
132                 ctf_oneflag = neutralflag = 0; // disable neutral flag in hud editor?
133         }
134
135         // when status CHANGES, set old status into prevstatus and current status into status
136         #define X(team) MACRO_BEGIN {                                                                                                   \
137                 if (team##flag != team##flag_prevframe) {                                                                       \
138                 team##flag_statuschange_time = time;                                                                    \
139                 team##flag_prevstatus = team##flag_prevframe;                                                   \
140                 team##flag_prevframe = team##flag;                                                                              \
141         }                                                                                                                                                       \
142         team##flag_statuschange_elapsedtime = time - team##flag_statuschange_time;      \
143     } MACRO_END
144         X(red);
145         X(blue);
146         X(yellow);
147         X(pink);
148         X(neutral);
149         #undef X
150
151         const float BLINK_FACTOR = 0.15;
152         const float BLINK_BASE = 0.85;
153         // note:
154         //   RMS = sqrt(BLINK_BASE^2 + 0.5 * BLINK_FACTOR^2)
155         // thus
156         //   BLINK_BASE = sqrt(RMS^2 - 0.5 * BLINK_FACTOR^2)
157         // ensure RMS == 1
158         const float BLINK_FREQ = 5; // circle frequency, = 2*pi*frequency in hertz
159
160         #define X(team, cond) \
161         string team##_icon = string_null, team##_icon_prevstatus = string_null; \
162         int team##_alpha, team##_alpha_prevstatus; \
163         team##_alpha = team##_alpha_prevstatus = 1; \
164         MACRO_BEGIN { \
165                 switch (team##flag) { \
166                         case 1: team##_icon = "flag_" #team "_taken"; break; \
167                         case 2: team##_icon = "flag_" #team "_lost"; break; \
168                         case 3: team##_icon = "flag_" #team "_carrying"; team##_alpha = BLINK_BASE + BLINK_FACTOR * cos(time * BLINK_FREQ); break; \
169                         default: \
170                                 if ((stat_items & CTF_SHIELDED) && (cond)) { \
171                                         team##_icon = "flag_" #team "_shielded"; \
172                                 } else { \
173                                         team##_icon = string_null; \
174                                 } \
175                                 break; \
176                 } \
177                 switch (team##flag_prevstatus) { \
178                         case 1: team##_icon_prevstatus = "flag_" #team "_taken"; break; \
179                         case 2: team##_icon_prevstatus = "flag_" #team "_lost"; break; \
180                         case 3: team##_icon_prevstatus = "flag_" #team "_carrying"; team##_alpha_prevstatus = BLINK_BASE + BLINK_FACTOR * cos(time * BLINK_FREQ); break; \
181                         default: \
182                                 if (team##flag == 3) { \
183                                         team##_icon_prevstatus = "flag_" #team "_carrying"; /* make it more visible */\
184                                 } else if((stat_items & CTF_SHIELDED) && (cond)) { \
185                                         team##_icon_prevstatus = "flag_" #team "_shielded"; \
186                                 } else { \
187                                         team##_icon_prevstatus = string_null; \
188                                 } \
189                                 break; \
190                 } \
191         } MACRO_END
192         X(red, myteam != NUM_TEAM_1 && (nteams & BIT(0)));
193         X(blue, myteam != NUM_TEAM_2 && (nteams & BIT(1)));
194         X(yellow, myteam != NUM_TEAM_3 && (nteams & BIT(2)));
195         X(pink, myteam != NUM_TEAM_4 && (nteams & BIT(3)));
196         X(neutral, ctf_oneflag);
197         #undef X
198
199         int tcount = 2;
200         if(nteams & BIT(2))
201                 tcount = 3;
202         if(nteams & BIT(3))
203                 tcount = 4;
204
205         if (ctf_oneflag) {
206                 // hacky, but these aren't needed
207                 red_icon = red_icon_prevstatus = blue_icon = blue_icon_prevstatus = yellow_icon = yellow_icon_prevstatus = pink_icon = pink_icon_prevstatus = string_null;
208                 fs = fs2 = fs3 = 1;
209         } else switch (tcount) {
210                 default:
211                 case 2: fs = 0.5; fs2 = 0.5; fs3 = 0.5; break;
212                 case 3: fs = 1; fs2 = 0.35; fs3 = 0.35; break;
213                 case 4: fs = 0.75; fs2 = 0.25; fs3 = 0.5; break;
214         }
215
216         if (mySize_x > mySize_y) {
217                 size1 = mySize_x;
218                 size2 = mySize_y;
219                 e1 = eX;
220                 e2 = eY;
221         } else {
222                 size1 = mySize_y;
223                 size2 = mySize_x;
224                 e1 = eY;
225                 e2 = eX;
226         }
227
228         switch (myteam) {
229                 default:
230                 case NUM_TEAM_1: {
231                         redflag_pos = pos;
232                         blueflag_pos = pos + eX * fs2 * size1;
233                         yellowflag_pos = pos - eX * fs2 * size1;
234                         pinkflag_pos = pos + eX * fs3 * size1;
235                         break;
236                 }
237                 case NUM_TEAM_2: {
238                         redflag_pos = pos + eX * fs2 * size1;
239                         blueflag_pos = pos;
240                         yellowflag_pos = pos - eX * fs2 * size1;
241                         pinkflag_pos = pos + eX * fs3 * size1;
242                         break;
243                 }
244                 case NUM_TEAM_3: {
245                         redflag_pos = pos + eX * fs3 * size1;
246                         blueflag_pos = pos - eX * fs2 * size1;
247                         yellowflag_pos = pos;
248                         pinkflag_pos = pos + eX * fs2 * size1;
249                         break;
250                 }
251                 case NUM_TEAM_4: {
252                         redflag_pos = pos - eX * fs2 * size1;
253                         blueflag_pos = pos + eX * fs3 * size1;
254                         yellowflag_pos = pos + eX * fs2 * size1;
255                         pinkflag_pos = pos;
256                         break;
257                 }
258         }
259         neutralflag_pos = pos;
260         flag_size = e1 * fs * size1 + e2 * size2;
261
262         #define X(team) MACRO_BEGIN { \
263                 f = bound(0, team##flag_statuschange_elapsedtime * 2, 1); \
264                 if (team##_icon && ctf_stalemate) \
265                         drawpic_aspect_skin(team##flag_pos, "flag_stalemate", flag_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); \
266                 if (team##_icon_prevstatus && f < 1) \
267                         drawpic_aspect_skin_expanding(team##flag_pos, team##_icon_prevstatus, flag_size, '1 1 1', panel_fg_alpha * team##_alpha_prevstatus, DRAWFLAG_NORMAL, f); \
268                 if (team##_icon) \
269                         drawpic_aspect_skin(team##flag_pos, team##_icon, flag_size, '1 1 1', panel_fg_alpha * team##_alpha * f, DRAWFLAG_NORMAL); \
270         } MACRO_END
271         X(red);
272         X(blue);
273         X(yellow);
274         X(pink);
275         X(neutral);
276         #undef X
277 }
278
279 // Keyhunt HUD modicon section
280 vector KH_SLOTS[4];
281
282 void HUD_Mod_KH(vector pos, vector mySize)
283 {
284         mod_active = 1; // keyhunt should never hide the mod icons panel
285
286         // Read current state
287         int state = STAT(KH_KEYS);
288         if(!state) return;
289
290         int i, key_state;
291         int all_keys, team1_keys, team2_keys, team3_keys, team4_keys, dropped_keys, carrying_keys;
292         all_keys = team1_keys = team2_keys = team3_keys = team4_keys = dropped_keys = carrying_keys = 0;
293
294         for(i = 0; i < 4; ++i)
295         {
296                 key_state = (bitshift(state, i * -5) & 31) - 1;
297
298                 if(key_state == -1)
299                         continue;
300
301                 if(key_state == 30)
302                 {
303                         ++carrying_keys;
304                         key_state = myteam;
305                 }
306
307                 switch(key_state)
308                 {
309                         case NUM_TEAM_1: ++team1_keys; break;
310                         case NUM_TEAM_2: ++team2_keys; break;
311                         case NUM_TEAM_3: ++team3_keys; break;
312                         case NUM_TEAM_4: ++team4_keys; break;
313                         case 29: ++dropped_keys; break;
314                 }
315
316                 ++all_keys;
317         }
318
319         // Calculate slot measurements
320         vector slot_size;
321         if(all_keys == 4 && mySize.x * 0.5 < mySize.y && mySize.y * 0.5 < mySize.x)
322         {
323                 // Quadratic arrangement
324                 slot_size = vec2(mySize.x * 0.5, mySize.y * 0.5);
325                 KH_SLOTS[0] = pos;
326                 KH_SLOTS[1] = pos + eX * slot_size.x;
327                 KH_SLOTS[2] = pos + eY * slot_size.y;
328                 KH_SLOTS[3] = pos + eX * slot_size.x + eY * slot_size.y;
329         }
330         else
331         {
332                 if(mySize.x > mySize.y)
333                 {
334                         // Horizontal arrangement
335                         slot_size = vec2(mySize.x / all_keys, mySize.y);
336                         for(i = 0; i < all_keys; ++i)
337                                 KH_SLOTS[i] = pos + eX * slot_size.x * i;
338                 }
339                 else
340                 {
341                         // Vertical arrangement
342                         slot_size = vec2(mySize.x, mySize.y / all_keys);
343                         for(i = 0; i < all_keys; ++i)
344                                 KH_SLOTS[i] = pos + eY * slot_size.y * i;
345                 }
346         }
347
348         // Make icons blink in case of RUN HERE
349
350         float alpha = 1;
351         if(carrying_keys)
352         {
353                 float blink = 0.6 + sin(2 * M_PI * time) * 0.4; // Oscillate between 0.2 and 1
354                 switch(myteam)
355                 {
356                         case NUM_TEAM_1: if(team1_keys == all_keys) alpha = blink; break;
357                         case NUM_TEAM_2: if(team2_keys == all_keys) alpha = blink; break;
358                         case NUM_TEAM_3: if(team3_keys == all_keys) alpha = blink; break;
359                         case NUM_TEAM_4: if(team4_keys == all_keys) alpha = blink; break;
360                 }
361         }
362
363         // Draw icons
364
365         i = 0;
366
367         while(team1_keys--)
368                 if(myteam == NUM_TEAM_1 && carrying_keys)
369                 {
370                         drawpic_aspect_skin(KH_SLOTS[i++], "kh_red_carrying", slot_size, '1 1 1', alpha, DRAWFLAG_NORMAL);
371                         --carrying_keys;
372                 }
373                 else
374                         drawpic_aspect_skin(KH_SLOTS[i++], "kh_red_taken", slot_size, '1 1 1', alpha, DRAWFLAG_NORMAL);
375
376         while(team2_keys--)
377                 if(myteam == NUM_TEAM_2 && carrying_keys)
378                 {
379                         drawpic_aspect_skin(KH_SLOTS[i++], "kh_blue_carrying", slot_size, '1 1 1', alpha, DRAWFLAG_NORMAL);
380                         --carrying_keys;
381                 }
382                 else
383                         drawpic_aspect_skin(KH_SLOTS[i++], "kh_blue_taken", slot_size, '1 1 1', alpha, DRAWFLAG_NORMAL);
384
385         while(team3_keys--)
386                 if(myteam == NUM_TEAM_3 && carrying_keys)
387                 {
388                         drawpic_aspect_skin(KH_SLOTS[i++], "kh_yellow_carrying", slot_size, '1 1 1', alpha, DRAWFLAG_NORMAL);
389                         --carrying_keys;
390                 }
391                 else
392                         drawpic_aspect_skin(KH_SLOTS[i++], "kh_yellow_taken", slot_size, '1 1 1', alpha, DRAWFLAG_NORMAL);
393
394         while(team4_keys--)
395                 if(myteam == NUM_TEAM_4 && carrying_keys)
396                 {
397                         drawpic_aspect_skin(KH_SLOTS[i++], "kh_pink_carrying", slot_size, '1 1 1', alpha, DRAWFLAG_NORMAL);
398                         --carrying_keys;
399                 }
400                 else
401                         drawpic_aspect_skin(KH_SLOTS[i++], "kh_pink_taken", slot_size, '1 1 1', alpha, DRAWFLAG_NORMAL);
402
403         while(dropped_keys--)
404                 drawpic_aspect_skin(KH_SLOTS[i++], "kh_dropped", slot_size, '1 1 1', alpha, DRAWFLAG_NORMAL);
405 }
406
407 // Keepaway HUD mod icon
408 int kaball_prevstatus; // last remembered status
409 float kaball_statuschange_time; // time when the status changed
410
411 // we don't need to reset for keepaway since it immediately
412 // autocorrects prevstatus as to if the player has the ball or not
413
414 void HUD_Mod_Keepaway(vector pos, vector mySize)
415 {
416         mod_active = 1; // keepaway should always show the mod HUD
417
418         float BLINK_FACTOR = 0.15;
419         float BLINK_BASE = 0.85;
420         float BLINK_FREQ = 5;
421         float kaball_alpha = BLINK_BASE + BLINK_FACTOR * cos(time * BLINK_FREQ);
422
423         int stat_items = STAT(ITEMS);
424         int kaball = (stat_items/IT_KEY1) & 1;
425
426         if(kaball != kaball_prevstatus)
427         {
428                 kaball_statuschange_time = time;
429                 kaball_prevstatus = kaball;
430         }
431
432         vector kaball_pos, kaball_size;
433
434         if(mySize.x > mySize.y) {
435                 kaball_pos = pos + eX * 0.25 * mySize.x;
436                 kaball_size = vec2(0.5 * mySize.x, mySize.y);
437         } else {
438                 kaball_pos = pos + eY * 0.25 * mySize.y;
439                 kaball_size = vec2(mySize.x, 0.5 * mySize.y);
440         }
441
442         float kaball_statuschange_elapsedtime = time - kaball_statuschange_time;
443         float f = bound(0, kaball_statuschange_elapsedtime*2, 1);
444
445         if(kaball_prevstatus && f < 1)
446                 drawpic_aspect_skin_expanding(kaball_pos, "keepawayball_carrying", kaball_size, '1 1 1', panel_fg_alpha * kaball_alpha, DRAWFLAG_NORMAL, f);
447
448         if(kaball)
449                 drawpic_aspect_skin(pos, "keepawayball_carrying", vec2(mySize.x, mySize.y), '1 1 1', panel_fg_alpha * kaball_alpha * f, DRAWFLAG_NORMAL);
450 }
451
452
453 // Nexball HUD mod icon
454 void HUD_Mod_NexBall(vector pos, vector mySize)
455 {
456         float nb_pb_starttime, dt, p;
457         int stat_items;
458
459         stat_items = STAT(ITEMS);
460         nb_pb_starttime = STAT(NB_METERSTART);
461
462         if (stat_items & IT_KEY1)
463                 mod_active = 1;
464         else
465                 mod_active = 0;
466
467         //Manage the progress bar if any
468         if (nb_pb_starttime > 0)
469         {
470                 dt = (time - nb_pb_starttime) % nb_pb_period;
471                 // one period of positive triangle
472                 p = 2 * dt / nb_pb_period;
473                 if (p > 1)
474                         p = 2 - p;
475
476                 HUD_Panel_DrawProgressBar(pos, mySize, "progressbar", p, (mySize.x <= mySize.y), 0, autocvar_hud_progressbar_nexball_color, autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL);
477         }
478
479         if (stat_items & IT_KEY1)
480                 drawpic_aspect_skin(pos, "nexball_carrying", eX * mySize.x + eY * mySize.y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
481 }
482
483 // Race/CTS HUD mod icons
484 float crecordtime_prev; // last remembered crecordtime
485 float crecordtime_change_time; // time when crecordtime last changed
486 float srecordtime_prev; // last remembered srecordtime
487 float srecordtime_change_time; // time when srecordtime last changed
488
489 float race_status_time;
490 int race_status_prev;
491 string race_status_name_prev;
492
493 // Check if the given name already exist in race rankings? In that case, where? (otherwise return 0)
494 int race_CheckName(string net_name)
495 {
496         int i;
497         for (i=RANKINGS_CNT-1;i>=0;--i)
498                 if(strdecolorize(grecordholder[i]) == strdecolorize(net_name))
499                         return i+1;
500         return 0;
501 }
502
503 void race_showTime(string text, vector pos, vector timeText_ofs, float theTime, vector textSize, float f)
504 {
505         drawstring_aspect(pos, text, textSize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
506         drawstring_aspect(pos + timeText_ofs, TIME_ENCODED_TOSTRING(theTime), textSize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
507         if (f < 1) {
508                 drawstring_aspect_expanding(pos, text, textSize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL, f);
509                 drawstring_aspect_expanding(pos + timeText_ofs, TIME_ENCODED_TOSTRING(theTime), textSize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL, f);
510         }
511 }
512
513 void HUD_Mod_Race(vector pos, vector mySize)
514 {
515         entity me;
516         me = playerslots[player_localnum];
517         float score;
518         score = me.(scores(ps_primary));
519
520         if(!(scores_flags(ps_primary) & SFL_TIME) || teamplay) // race/cts record display on HUD
521         {
522                 mod_active = 0; // hide it in this case!
523                 return; // no records in the actual race
524         }
525
526         mod_active = 1;
527
528         // clientside personal record
529         string rr;
530         if(gametype == MAPINFO_TYPE_CTS)
531                 rr = CTS_RECORD;
532         else
533                 rr = RACE_RECORD;
534         float t = stof(db_get(ClientProgsDB, strcat(shortmapname, rr, "time")));
535
536         if(score && (score < t || !t)) {
537                 db_put(ClientProgsDB, strcat(shortmapname, rr, "time"), ftos(score));
538                 if(autocvar_cl_autodemo_delete_keeprecords)
539                 {
540                         float f = autocvar_cl_autodemo_delete;
541                         f &= ~1;
542                         cvar_set("cl_autodemo_delete", ftos(f)); // don't delete demo with new record!
543                 }
544         }
545
546         if(t != crecordtime_prev) {
547                 crecordtime_prev = t;
548                 crecordtime_change_time = time;
549         }
550
551         vector textPos, medalPos;
552         float squareSize;
553         if(mySize.x > mySize.y) {
554                 // text on left side
555                 squareSize = min(mySize.y, mySize.x/2);
556                 vector ofs = vec2(0.5 * max(0, mySize.x/2 - squareSize), 0.5 * (mySize.y - squareSize));
557                 textPos = pos + ofs;
558                 ofs.x += 0.5 * mySize.x;
559                 medalPos = pos + ofs;
560         } else {
561                 // text on top
562                 squareSize = min(mySize.x, mySize.y/2);
563                 vector ofs = vec2(0.5 * (mySize.x - squareSize), 0.5 * max(0, mySize.y/2 - squareSize));
564                 textPos = pos + ofs;
565                 ofs.y += 0.5 * mySize.y;
566                 medalPos = pos + ofs;
567         }
568         vector textSize = vec2(squareSize, 0.25 * squareSize);
569
570         race_showTime(_("Personal best"), textPos, eY * 0.25 * squareSize, t, textSize, time - crecordtime_change_time);
571
572         // server record
573         t = race_server_record;
574         if(t != srecordtime_prev) {
575                 srecordtime_prev = t;
576                 srecordtime_change_time = time;
577         }
578
579         textPos += eY * 0.5 * squareSize;
580         race_showTime(_("Server best"), textPos, eY * 0.25 * squareSize, t, textSize, time - srecordtime_change_time);
581
582         if (race_status != race_status_prev || race_status_name != race_status_name_prev) {
583                 race_status_time = time + 5;
584                 race_status_prev = race_status;
585                 if (race_status_name_prev)
586                         strunzone(race_status_name_prev);
587                 race_status_name_prev = strzone(race_status_name);
588         }
589
590         // race "awards"
591         float a;
592         a = bound(0, race_status_time - time, 1);
593
594         string s;
595         s = textShortenToWidth(ColorTranslateRGB(race_status_name), squareSize, '1 1 0' * 0.1 * squareSize, stringwidth_colors);
596
597         float rank;
598         if(race_status > 0)
599                 rank = race_CheckName(race_status_name);
600         else
601                 rank = 0;
602         string rankname;
603         rankname = count_ordinal(rank);
604
605         vector namepos;
606         namepos = medalPos + '0 0.8 0' * squareSize;
607         vector rankpos;
608         rankpos = medalPos + '0 0.15 0' * squareSize;
609
610         if(race_status == 0)
611                 drawpic_aspect_skin(medalPos, "race_newfail", '1 1 0' * squareSize, '1 1 1', panel_fg_alpha * a, DRAWFLAG_NORMAL);
612         else if(race_status == 1) {
613                 drawpic_aspect_skin(medalPos + '0.1 0 0' * squareSize, "race_newtime", '1 1 0' * 0.8 * squareSize, '1 1 1', panel_fg_alpha * a, DRAWFLAG_NORMAL);
614                 drawcolorcodedstring_aspect(namepos, s, '1 0.2 0' * squareSize, panel_fg_alpha * a, DRAWFLAG_NORMAL);
615                 drawstring_aspect(rankpos, rankname, '1 0.15 0' * squareSize, '1 1 1', panel_fg_alpha * a, DRAWFLAG_NORMAL);
616         } else if(race_status == 2) {
617                 if(strdecolorize(race_status_name) == strdecolorize(entcs_GetName(player_localnum)) || !race_myrank || race_myrank < rank)
618                         drawpic_aspect_skin(medalPos + '0.1 0 0' * squareSize, "race_newrankgreen", '1 1 0' * 0.8 * squareSize, '1 1 1', panel_fg_alpha * a, DRAWFLAG_NORMAL);
619                 else
620                         drawpic_aspect_skin(medalPos + '0.1 0 0' * squareSize, "race_newrankyellow", '1 1 0' * 0.8 * squareSize, '1 1 1', panel_fg_alpha * a, DRAWFLAG_NORMAL);
621                 drawcolorcodedstring_aspect(namepos, s, '1 0.2 0' * squareSize, panel_fg_alpha * a, DRAWFLAG_NORMAL);
622                 drawstring_aspect(rankpos, rankname, '1 0.15 0' * squareSize, '1 1 1', panel_fg_alpha * a, DRAWFLAG_NORMAL);
623         } else if(race_status == 3) {
624                 drawpic_aspect_skin(medalPos + '0.1 0 0' * squareSize, "race_newrecordserver", '1 1 0' * 0.8 * squareSize, '1 1 1', panel_fg_alpha * a, DRAWFLAG_NORMAL);
625                 drawcolorcodedstring_aspect(namepos, s, '1 0.2 0' * squareSize, panel_fg_alpha * a, DRAWFLAG_NORMAL);
626                 drawstring_aspect(rankpos, rankname, '1 0.15 0' * squareSize, '1 1 1', panel_fg_alpha * a, DRAWFLAG_NORMAL);
627         }
628
629         if (race_status_time - time <= 0) {
630                 race_status_prev = -1;
631                 race_status = -1;
632                 if(race_status_name)
633                         strunzone(race_status_name);
634                 race_status_name = string_null;
635                 if(race_status_name_prev)
636                         strunzone(race_status_name_prev);
637                 race_status_name_prev = string_null;
638         }
639 }
640
641 void DrawDomItem(vector myPos, vector mySize, float aspect_ratio, int layout, int i)
642 {
643     TC(int, layout); TC(int, i);
644         float stat = -1;
645         string pic = "";
646         vector color = '0 0 0';
647         switch(i)
648         {
649                 case 0: stat = STAT(DOM_PPS_RED); pic = "dom_icon_red"; color = '1 0 0'; break;
650                 case 1: stat = STAT(DOM_PPS_BLUE); pic = "dom_icon_blue"; color = '0 0 1'; break;
651                 case 2: stat = STAT(DOM_PPS_YELLOW); pic = "dom_icon_yellow"; color = '1 1 0'; break;
652                 default:
653                 case 3: stat = STAT(DOM_PPS_PINK); pic = "dom_icon_pink"; color = '1 0 1'; break;
654         }
655         float pps_ratio = 0;
656         if(STAT(DOM_TOTAL_PPS))
657                 pps_ratio = stat / STAT(DOM_TOTAL_PPS);
658
659         if(mySize.x/mySize.y > aspect_ratio)
660         {
661                 i = aspect_ratio * mySize.y;
662                 myPos.x = myPos.x + (mySize.x - i) / 2;
663                 mySize.x = i;
664         }
665         else
666         {
667                 i = 1/aspect_ratio * mySize.x;
668                 myPos.y = myPos.y + (mySize.y - i) / 2;
669                 mySize.y = i;
670         }
671
672         if (layout) // show text too
673         {
674                 //draw the text
675                 color *= 0.5 + pps_ratio * (1 - 0.5); // half saturated color at min, full saturated at max
676                 if (layout == 2) // average pps
677                         drawstring_aspect(myPos + eX * mySize.y, ftos_decimals(stat, 2), vec2((2/3) * mySize.x, mySize.y), color, panel_fg_alpha, DRAWFLAG_NORMAL);
678                 else // percentage of average pps
679                         drawstring_aspect(myPos + eX * mySize.y, strcat( ftos(floor(pps_ratio*100 + 0.5)), "%" ), vec2((2/3) * mySize.x, mySize.y), color, panel_fg_alpha, DRAWFLAG_NORMAL);
680         }
681
682         //draw the icon
683         drawpic_aspect_skin(myPos, pic, '1 1 0' * mySize.y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
684         if (stat > 0)
685         {
686                 drawsetcliparea(myPos.x, myPos.y + mySize.y * (1 - pps_ratio), mySize.y, mySize.y * pps_ratio);
687                 drawpic_aspect_skin(myPos, strcat(pic, "-highlighted"), '1 1 0' * mySize.y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
688                 drawresetcliparea();
689         }
690 }
691
692 void HUD_Mod_Dom(vector myPos, vector mySize)
693 {
694         mod_active = 1; // required in each mod function that always shows something
695
696         int layout = autocvar_hud_panel_modicons_dom_layout;
697         int rows, columns;
698         float aspect_ratio;
699         aspect_ratio = (layout) ? 3 : 1;
700         rows = HUD_GetRowCount(team_count, mySize, aspect_ratio);
701         columns = ceil(team_count/rows);
702
703         int i;
704         float row = 0, column = 0;
705         vector pos, itemSize;
706         itemSize = vec2(mySize.x / columns, mySize.y / rows);
707         for(i=0; i<team_count; ++i)
708         {
709                 pos = myPos + vec2(column * itemSize.x, row * itemSize.y);
710
711                 DrawDomItem(pos, itemSize, aspect_ratio, layout, i);
712
713                 ++row;
714                 if(row >= rows)
715                 {
716                         row = 0;
717                         ++column;
718                 }
719         }
720 }
721
722 void HUD_ModIcons_SetFunc()
723 {
724         HUD_ModIcons_GameType = gametype.m_modicons;
725 }
726
727 float mod_alpha;
728
729 void HUD_ModIcons()
730 {
731         if(!autocvar__hud_configure)
732         {
733                 if(!autocvar_hud_panel_modicons) return;
734                 if(!HUD_ModIcons_GameType) return;
735         }
736
737         if(mod_active || autocvar__hud_configure)
738                 mod_alpha = min(mod_alpha + frametime * 2, 1);
739         else
740                 mod_alpha = max(mod_alpha - frametime * 2, 0);
741
742         //if(mod_alpha <= 0)
743         //      return;
744         panel_fade_alpha *= mod_alpha;
745         HUD_Panel_LoadCvars();
746
747         draw_beginBoldFont();
748
749         if (autocvar_hud_panel_modicons_dynamichud)
750                 HUD_Scale_Enable();
751         else
752                 HUD_Scale_Disable();
753
754         HUD_Panel_DrawBg();
755
756         if(panel_bg_padding)
757         {
758                 panel_pos += '1 1 0' * panel_bg_padding;
759                 panel_size -= '2 2 0' * panel_bg_padding;
760         }
761
762         if(autocvar__hud_configure)
763                 HUD_Mod_CTF(panel_pos, panel_size);
764         else
765                 HUD_ModIcons_GameType(panel_pos, panel_size);
766
767         draw_endBoldFont();
768 }