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