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