]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/client/miscfunctions.qc
Merge branch 'master' into TimePath/experiments/csqc_prediction (blame TimePath if...
[xonotic/xonotic-data.pk3dir.git] / qcsrc / client / miscfunctions.qc
1 // WARNING: this kills the trace globals
2 float WarpZoneLib_ExactTrigger_Touch();
3 #define EXACTTRIGGER_TOUCH if(WarpZoneLib_ExactTrigger_Touch()) return
4
5 entity players;
6 entity teams;
7 var float team_count; // real teams
8
9 void AuditLists()
10 {
11         entity e;
12         entity prev;
13
14         prev = players;
15         for(e = prev.sort_next; e; prev = e, e = e.sort_next)
16         {
17                 if(prev != e.sort_prev)
18                         error(strcat("sort list chain error\nplease submit the output of 'prvm_edicts client' to the developers"));
19         }
20
21         prev = teams;
22         for(e = prev.sort_next; e; prev = e, e = e.sort_next)
23         {
24                 if(prev != e.sort_prev)
25                         error(strcat("sort list chain error\nplease submit the output of 'prvm_edicts client' to the developers"));
26         }
27 }
28
29
30 float RegisterPlayer(entity player)
31 {
32         entity pl;
33         AuditLists();
34         for(pl = players.sort_next; pl; pl = pl.sort_next)
35                 if(pl == player)
36                         error("Player already registered!");
37         player.sort_next = players.sort_next;
38         player.sort_prev = players;
39         if(players.sort_next)
40                 players.sort_next.sort_prev = player;
41         players.sort_next = player;
42         AuditLists();
43         return true;
44 }
45
46 void RemovePlayer(entity player)
47 {
48         entity pl, parent;
49         AuditLists();
50         parent = players;
51         for(pl = players.sort_next; pl && pl != player; pl = pl.sort_next)
52                 parent = pl;
53
54         if(!pl)
55         {
56                 error("Trying to remove a player which is not in the playerlist!");
57                 return;
58         }
59         parent.sort_next = player.sort_next;
60         if(player.sort_next)
61                 player.sort_next.sort_prev = parent;
62         AuditLists();
63 }
64
65 void MoveToLast(entity e)
66 {
67         AuditLists();
68         other = e.sort_next;
69         while(other)
70         {
71                 SORT_SWAP(other, e);
72                 other = e.sort_next;
73         }
74         AuditLists();
75 }
76
77 float RegisterTeam(entity Team)
78 {
79         entity tm;
80         AuditLists();
81         for(tm = teams.sort_next; tm; tm = tm.sort_next)
82                 if(tm == Team)
83                         error("Team already registered!");
84         Team.sort_next = teams.sort_next;
85         Team.sort_prev = teams;
86         if(teams.sort_next)
87                 teams.sort_next.sort_prev = Team;
88         teams.sort_next = Team;
89         if(Team.team && Team.team != NUM_SPECTATOR)
90                 ++team_count;
91         AuditLists();
92         return true;
93 }
94
95 void RemoveTeam(entity Team)
96 {
97         entity tm, parent;
98         AuditLists();
99         parent = teams;
100         for(tm = teams.sort_next; tm && tm != Team; tm = tm.sort_next)
101                 parent = tm;
102
103         if(!tm)
104         {
105                 print(_("Trying to remove a team which is not in the teamlist!"));
106                 return;
107         }
108         parent.sort_next = Team.sort_next;
109         if(Team.sort_next)
110                 Team.sort_next.sort_prev = parent;
111         if(Team.team && Team.team != NUM_SPECTATOR)
112                 --team_count;
113         AuditLists();
114 }
115
116 entity GetTeam(float Team, float add)
117 {
118         float num;
119         entity tm;
120         num = (Team == NUM_SPECTATOR) ? 16 : Team;
121         if(teamslots[num])
122                 return teamslots[num];
123         if (!add)
124                 return world;
125         tm = spawn();
126         tm.team = Team;
127         teamslots[num] = tm;
128         RegisterTeam(tm);
129         return tm;
130 }
131
132 vector HUD_GetFontsize(string cvarname)
133 {
134         vector v;
135         v = stov(cvar_string(cvarname));
136         if(v_x == 0)
137                 v = '8 8 0';
138         if(v_y == 0)
139                 v_y = v_x;
140         v_z = 0;
141         return v;
142 }
143
144 float PreviewExists(string name)
145 {
146         if(autocvar_cl_readpicture_force)
147                 return false;
148
149         if (fexists(strcat(name, ".tga"))) return true;
150         if (fexists(strcat(name, ".png"))) return true;
151         if (fexists(strcat(name, ".jpg"))) return true;
152         if (fexists(strcat(name, ".pcx"))) return true;
153
154         return false;
155 }
156
157 vector rotate(vector v, float a)
158 {
159         vector w = '0 0 0';
160         // FTEQCC SUCKS AGAIN
161         w_x =      v_x * cos(a) + v_y * sin(a);
162         w_y = -1 * v_x * sin(a) + v_y * cos(a);
163         return w;
164 }
165
166 float ColorTranslateMode;
167
168 string ColorTranslateRGB(string s)
169 {
170         if(ColorTranslateMode & 1)
171                 return strdecolorize(s);
172         else
173                 return s;
174 }
175
176 // decolorizes and team colors the player name when needed
177 string playername(string thename, float teamid)
178 {
179     string t;
180     if (teamplay)
181     {
182         t = Team_ColorCode(teamid);
183         return strcat(t, strdecolorize(thename));
184     }
185     else
186         return strdecolorize(thename);
187 }
188
189 float cvar_or(string cv, float v)
190 {
191         string s;
192         s = cvar_string(cv);
193         if(s == "")
194                 return v;
195         else
196                 return stof(s);
197 }
198
199 vector project_3d_to_2d(vector vec)
200 {
201         vec = cs_project(vec);
202         if(cs_project_is_b0rked > 0)
203         {
204                 vec_x *= vid_conwidth / vid_width;
205                 vec_y *= vid_conheight / vid_height;
206         }
207         return vec;
208 }
209
210 void dummyfunction(float a1, float a2, float a3, float a4, float a5, float a6, float a7, float a8)
211 {
212 }
213
214 float expandingbox_sizefactor_from_fadelerp(float fadelerp)
215 {
216         return 1.2 / (1.2 - fadelerp);
217 }
218
219 vector expandingbox_resize_centered_box_offset(float sz, vector boxsize, float boxxsizefactor)
220 {
221         boxsize_x *= boxxsizefactor; // easier interface for text
222         return boxsize * (0.5 * (1 - sz));
223 }
224
225 void drawborderlines(float thickness, vector pos, vector dim, vector color, float theAlpha, float drawflag)
226 {
227         vector line_dim = '0 0 0';
228
229         // left and right lines
230         pos_x -= thickness;
231         line_dim_x = thickness;
232         line_dim_y = dim_y;
233         drawfill(pos, line_dim, color, theAlpha, drawflag);
234         drawfill(pos + (dim_x + thickness) * '1 0 0', line_dim, color, theAlpha, drawflag);
235
236         // upper and lower lines
237         pos_y -= thickness;
238         line_dim_x = dim_x + thickness * 2; // make upper and lower lines longer
239         line_dim_y = thickness;
240         drawfill(pos, line_dim, color, theAlpha, drawflag);
241         drawfill(pos + (dim_y + thickness) * '0 1 0', line_dim, color, theAlpha, drawflag);
242 }
243
244 void drawpic_tiled(vector pos, string pic, vector sz, vector area, vector color, float theAlpha, float drawflag)
245 {
246         vector current_pos = '0 0 0', end_pos, new_size = '0 0 0', ratio = '0 0 0';
247         end_pos = pos + area;
248
249         current_pos_y = pos_y;
250         while (current_pos_y < end_pos_y)
251         {
252                 current_pos_x = pos_x;
253                 while (current_pos_x < end_pos_x)
254                 {
255                         new_size_x = min(sz_x, end_pos_x - current_pos_x);
256                         new_size_y = min(sz_y, end_pos_y - current_pos_y);
257                         ratio_x = new_size_x / sz_x;
258                         ratio_y = new_size_y / sz_y;
259                         drawsubpic(current_pos, new_size, pic, '0 0 0', ratio, color, theAlpha, drawflag);
260                         current_pos_x += sz_x;
261                 }
262                 current_pos_y += sz_y;
263         }
264 }
265
266 // drawpic wrapper to draw an image as large as possible with preserved aspect ratio into a box
267 var float _drawpic_imgaspect;
268 var vector _drawpic_imgsize;
269 var vector _drawpic_sz;
270 var float _drawpic_oldsz;
271 var string _drawpic_picpath;
272 #define drawpic_aspect(pos,pic,mySize,color,theAlpha,drawflag)\
273         do {\
274                 _drawpic_imgsize = draw_getimagesize(pic);\
275                 if(_drawpic_imgsize != '0 0 0') {\
276                         _drawpic_imgaspect = _drawpic_imgsize_x/_drawpic_imgsize_y;\
277                         _drawpic_sz = mySize;\
278                         if(_drawpic_sz_x/_drawpic_sz_y > _drawpic_imgaspect) {\
279                                 _drawpic_oldsz = _drawpic_sz_x;\
280                                 _drawpic_sz_x = _drawpic_sz_y * _drawpic_imgaspect;\
281                                 if(_drawpic_sz_x)\
282                                         drawpic(pos + eX * (_drawpic_oldsz - _drawpic_sz_x) * 0.5, pic, _drawpic_sz, color, theAlpha, drawflag);\
283                         } else {\
284                                 _drawpic_oldsz = _drawpic_sz_y;\
285                                 _drawpic_sz_y = _drawpic_sz_x / _drawpic_imgaspect;\
286                                 if(_drawpic_sz_y)\
287                                         drawpic(pos + eY * (_drawpic_oldsz - _drawpic_sz_y) * 0.5, pic, _drawpic_sz, color, theAlpha, drawflag);\
288                         }\
289                 }\
290         } while(0)
291
292 // draw HUD element with image from gfx/hud/hud_skin/foo.tga if it exists, otherwise gfx/hud/default/foo.tga
293 #define drawpic_aspect_skin(pos,pic,sz,color,theAlpha,drawflag)\
294         do{\
295                 _drawpic_picpath = strcat(hud_skin_path, "/", pic);\
296                 if(precache_pic(_drawpic_picpath) == "") {\
297                         _drawpic_picpath = strcat("gfx/hud/default/", pic);\
298                 }\
299                 drawpic_aspect(pos, _drawpic_picpath, sz, color, theAlpha, drawflag);\
300                 _drawpic_picpath = string_null;\
301         } while(0)
302
303 // draw HUD element with image from gfx/hud/hud_skin/foo.tga if it exists, otherwise gfx/hud/default/foo.tga
304 #define drawpic_skin(pos,pic,sz,color,theAlpha,drawflag)\
305         do{\
306                 _drawpic_picpath = strcat(hud_skin_path, "/", pic);\
307                 if(precache_pic(_drawpic_picpath) == "") {\
308                         _drawpic_picpath = strcat("gfx/hud/default/", pic);\
309                 }\
310                 drawpic(pos, _drawpic_picpath, sz, color, theAlpha, drawflag);\
311                 _drawpic_picpath = string_null;\
312         } while(0)
313
314 void drawpic_aspect_skin_expanding(vector position, string pic, vector theScale, vector rgb, float theAlpha, float flag, float fadelerp)
315 {
316         float sz;
317         sz = expandingbox_sizefactor_from_fadelerp(fadelerp);
318
319         drawpic_aspect_skin(position + expandingbox_resize_centered_box_offset(sz, theScale, 1), pic, theScale * sz, rgb, theAlpha * (1 - fadelerp), flag);
320 }
321
322 void drawpic_aspect_skin_expanding_two(vector position, string pic, vector theScale, vector rgb, float theAlpha, float flag, float fadelerp)
323 {
324         drawpic_aspect_skin_expanding(position, pic, theScale, rgb, theAlpha, flag, fadelerp);
325         drawpic_skin(position, pic, theScale, rgb, theAlpha * fadelerp, flag);
326 }
327 #define SET_POS_AND_SZ_Y_ASPECT(allow_colors)\
328         float textaspect, oldsz;\
329         textaspect = stringwidth(text, allow_colors, '1 1 1' * sz_y) / sz_y;\
330         if(sz_x/sz_y > textaspect) {\
331                 oldsz = sz_x;\
332                 sz_x = sz_y * textaspect;\
333                 pos_x += (oldsz - sz_x) * 0.5;\
334         } else {\
335                 oldsz = sz_y;\
336                 sz_y = sz_x / textaspect; \
337                 pos_y += (oldsz - sz_y) * 0.5;\
338         }
339
340 // drawstring wrapper to draw a string as large as possible with preserved aspect ratio into a box
341 void drawstring_aspect(vector pos, string text, vector sz, vector color, float theAlpha, float drawflag) {
342         SET_POS_AND_SZ_Y_ASPECT(FALSE)
343         drawstring(pos, text, '1 1 0' * sz_y, color, theAlpha, drawflag);
344 }
345
346 // drawstring wrapper to draw a colorcodedstring as large as possible with preserved aspect ratio into a box
347 void drawcolorcodedstring_aspect(vector pos, string text, vector sz, float theAlpha, float drawflag) {
348         SET_POS_AND_SZ_Y_ASPECT(TRUE)
349         drawcolorcodedstring(pos, text, '1 1 0' * sz_y, theAlpha, drawflag);
350 }
351
352 vector drawfontscale;
353 void drawstring_expanding(vector position, string text, vector theScale, vector rgb, float theAlpha, float flag, float fadelerp)
354 {
355         float sz;
356         sz = expandingbox_sizefactor_from_fadelerp(fadelerp);
357
358         drawfontscale = sz * '1 1 0';
359         dummyfunction(0, 0, 0, 0, 0, 0, 0, 0);
360         drawstring(position + expandingbox_resize_centered_box_offset(sz, theScale, stringwidth(text, FALSE, theScale * (sz / drawfontscale_x)) / (theScale_x * sz)), text, theScale * (sz / drawfontscale_x), rgb, theAlpha * (1 - fadelerp), flag);
361         // width parameter:
362         //    (scale_x * sz / drawfontscale_x) * drawfontscale_x * SIZE1 / (scale_x * sz)
363         //    SIZE1
364         drawfontscale = '1 1 0';
365 }
366
367 // drawstring wrapper to draw a string as large as possible with preserved aspect ratio into a box
368 void drawstring_aspect_expanding(vector pos, string text, vector sz, vector color, float theAlpha, float drawflag, float fadelerp) {
369         SET_POS_AND_SZ_Y_ASPECT(FALSE)
370         drawstring_expanding(pos, text, '1 1 0' * sz_y, color, theAlpha, drawflag, fadelerp);
371 }
372
373 void drawcolorcodedstring_expanding(vector position, string text, vector theScale, float theAlpha, float flag, float fadelerp)
374 {
375         float sz;
376         sz = expandingbox_sizefactor_from_fadelerp(fadelerp);
377
378         drawfontscale = sz * '1 1 0';
379         dummyfunction(0, 0, 0, 0, 0, 0, 0, 0);
380         drawcolorcodedstring(position + expandingbox_resize_centered_box_offset(sz, theScale, stringwidth(text, TRUE, theScale * (sz / drawfontscale_x)) / (theScale_x * sz)), text, theScale * (sz / drawfontscale_x), theAlpha * (1 - fadelerp), flag);
381         drawfontscale = '1 1 0';
382 }
383
384 void drawcolorcodedstring_aspect_expanding(vector pos, string text, vector sz, float theAlpha, float drawflag, float fadelerp) {
385         SET_POS_AND_SZ_Y_ASPECT(TRUE)
386         drawcolorcodedstring_expanding(pos, text, '1 1 0' * sz_y, theAlpha, drawflag, fadelerp);
387 }
388
389 // this draws the triangles of a model DIRECTLY. Don't expect high performance, really...
390 float PolyDrawModelSurface(entity e, float i_s)
391 {
392         float i_t;
393         float n_t;
394         vector tri;
395         string tex;
396         tex = getsurfacetexture(e, i_s);
397         if (!tex)
398                 return 0; // this is beyond the last one
399         n_t = getsurfacenumtriangles(e, i_s);
400         for(i_t = 0; i_t < n_t; ++i_t)
401         {
402                 tri = getsurfacetriangle(e, i_s, i_t);
403                 R_BeginPolygon(tex, 0);
404                 R_PolygonVertex(getsurfacepoint(e, i_s, tri_x), getsurfacepointattribute(e, i_s, tri_x, SPA_TEXCOORDS0), '1 1 1', 1);
405                 R_PolygonVertex(getsurfacepoint(e, i_s, tri_y), getsurfacepointattribute(e, i_s, tri_y, SPA_TEXCOORDS0), '1 1 1', 1);
406                 R_PolygonVertex(getsurfacepoint(e, i_s, tri_z), getsurfacepointattribute(e, i_s, tri_z, SPA_TEXCOORDS0), '1 1 1', 1);
407                 R_EndPolygon();
408         }
409         return 1;
410 }
411 void PolyDrawModel(entity e)
412 {
413         float i_s;
414         for(i_s = 0; ; ++i_s)
415                 if(!PolyDrawModelSurface(e, i_s))
416                         break;
417 }
418
419 void DrawCircleClippedPic(vector centre, float radius, string pic, float f, vector rgb, float a, float drawflag)
420 {
421         float x, y, q, d;
422         vector ringsize, v, t;
423         ringsize = radius * '1 1 0';
424
425         x = cos(f * 2 * M_PI);
426         y = sin(f * 2 * M_PI);
427         q = fabs(x) + fabs(y);
428         x /= q;
429         y /= q;
430
431         if(f >= 1)
432         {
433                 // draw full rectangle
434                 R_BeginPolygon(pic, drawflag);
435                         v = centre;                     t = '0.5 0.5 0';
436                         v_x += 0.5 * ringsize_x;        t += '0.5 0.5 0';
437                         R_PolygonVertex(v, t, rgb, a);
438
439                         v = centre;                     t = '0.5 0.5 0';
440                         v_y += 0.5 * ringsize_y;        t += '0.5 -0.5 0';
441                         R_PolygonVertex(v, t, rgb, a);
442
443                         v = centre;                     t = '0.5 0.5 0';
444                         v_x -= 0.5 * ringsize_x;        t -= '0.5 0.5 0';
445                         R_PolygonVertex(v, t, rgb, a);
446
447                         v = centre;                     t = '0.5 0.5 0';
448                         v_y -= 0.5 * ringsize_y;        t -= '0.5 -0.5 0';
449                         R_PolygonVertex(v, t, rgb, a);
450                 R_EndPolygon();
451
452                 d = q - 1;
453                 if(d > 0)
454                 {
455                         R_BeginPolygon(pic, drawflag);
456                                 v = centre;                     t = '0.5 0.5 0';
457                                 R_PolygonVertex(v, t, rgb, a);
458
459                                 v = centre;                     t = '0.5 0.5 0';
460                                 v_x += 0.5 * ringsize_x;        t += '0.5 0.5 0';
461                                 R_PolygonVertex(v, t, rgb, a);
462                 }
463         }
464         else if(f > 0.75)
465         {
466                 // draw upper and first triangle
467                 R_BeginPolygon(pic, drawflag);
468                         v = centre;                     t = '0.5 0.5 0';
469                         v_x += 0.5 * ringsize_x;        t += '0.5 0.5 0';
470                         R_PolygonVertex(v, t, rgb, a);
471
472                         v = centre;                     t = '0.5 0.5 0';
473                         v_y += 0.5 * ringsize_y;        t += '0.5 -0.5 0';
474                         R_PolygonVertex(v, t, rgb, a);
475
476                         v = centre;                     t = '0.5 0.5 0';
477                         v_x -= 0.5 * ringsize_x;        t -= '0.5 0.5 0';
478                         R_PolygonVertex(v, t, rgb, a);
479                 R_EndPolygon();
480                 R_BeginPolygon(pic, drawflag);
481                         v = centre;                     t = '0.5 0.5 0';
482                         R_PolygonVertex(v, t, rgb, a);
483
484                         v = centre;                     t = '0.5 0.5 0';
485                         v_x -= 0.5 * ringsize_x;        t -= '0.5 0.5 0';
486                         R_PolygonVertex(v, t, rgb, a);
487
488                         v = centre;                     t = '0.5 0.5 0';
489                         v_y -= 0.5 * ringsize_y;        t -= '0.5 -0.5 0';
490                         R_PolygonVertex(v, t, rgb, a);
491
492                 d = q - 0.75;
493                 if(d <= 0)
494                         R_EndPolygon();
495         }
496         else if(f > 0.5)
497         {
498                 // draw upper triangle
499                 R_BeginPolygon(pic, drawflag);
500                         v = centre;                     t = '0.5 0.5 0';
501                         v_x += 0.5 * ringsize_x;        t += '0.5 0.5 0';
502                         R_PolygonVertex(v, t, rgb, a);
503
504                         v = centre;                     t = '0.5 0.5 0';
505                         v_y += 0.5 * ringsize_y;        t += '0.5 -0.5 0';
506                         R_PolygonVertex(v, t, rgb, a);
507
508                         v = centre;                     t = '0.5 0.5 0';
509                         v_x -= 0.5 * ringsize_x;        t -= '0.5 0.5 0';
510                         R_PolygonVertex(v, t, rgb, a);
511                 R_EndPolygon();
512
513                 d = q - 0.5;
514                 if(d > 0)
515                 {
516                         R_BeginPolygon(pic, drawflag);
517                                 v = centre;                     t = '0.5 0.5 0';
518                                 R_PolygonVertex(v, t, rgb, a);
519
520                                 v = centre;                     t = '0.5 0.5 0';
521                                 v_x -= 0.5 * ringsize_x;        t -= '0.5 0.5 0';
522                                 R_PolygonVertex(v, t, rgb, a);
523                 }
524         }
525         else if(f > 0.25)
526         {
527                 // draw first triangle
528                 R_BeginPolygon(pic, drawflag);
529                         v = centre;                     t = '0.5 0.5 0';
530                         R_PolygonVertex(v, t, rgb, a);
531
532                         v = centre;                     t = '0.5 0.5 0';
533                         v_x += 0.5 * ringsize_x;        t += '0.5 0.5 0';
534                         R_PolygonVertex(v, t, rgb, a);
535
536                         v = centre;                     t = '0.5 0.5 0';
537                         v_y += 0.5 * ringsize_y;        t += '0.5 -0.5 0';
538                         R_PolygonVertex(v, t, rgb, a);
539
540                 d = q - 0.25;
541                 if(d <= 0)
542                         R_EndPolygon();
543         }
544         else
545         {
546                 d = q;
547                 if(d > 0)
548                 {
549                         R_BeginPolygon(pic, drawflag);
550                                 v = centre;                     t = '0.5 0.5 0';
551                                 R_PolygonVertex(v, t, rgb, a);
552
553                                 v = centre;                     t = '0.5 0.5 0';
554                                 v_x += 0.5 * ringsize_x;        t += '0.5 0.5 0';
555                                 R_PolygonVertex(v, t, rgb, a);
556                 }
557         }
558
559         if(d > 0)
560         {
561                         v = centre;                     t = '0.5 0.5 0';
562                         v_x += x * 0.5 * ringsize_x;    t += x * '0.5 0.5 0';
563                         v_y += y * 0.5 * ringsize_y;    t += y * '0.5 -0.5 0';
564                         R_PolygonVertex(v, t, rgb, a);
565                 R_EndPolygon();
566         }
567 }
568
569 const vector GETPLAYERORIGIN_ERROR = '1123581321 2357111317 3141592653'; // way out of bounds for anything on the map
570 vector getplayerorigin(float pl)
571 {
572         entity e;
573
574         e = CSQCModel_server2csqc(pl + 1);
575         if(e)
576                 return e.origin;
577
578         e = entcs_receiver[pl];
579         if(e)
580                 return e.origin;
581
582         return GETPLAYERORIGIN_ERROR;
583 }
584
585 float getplayeralpha(float pl)
586 {
587         entity e;
588
589         e = CSQCModel_server2csqc(pl + 1);
590         if(e)
591                 return e.alpha;
592
593         return 1;
594 }
595
596 vector getcsqcplayercolor(float pl)
597 {
598         entity e;
599
600         e = CSQCModel_server2csqc(pl);
601         if(e)
602         {
603                 if(e.colormap > 0)
604                         return colormapPaletteColor(((e.colormap >= 1024) ? e.colormap : stof(getplayerkeyvalue(e.colormap - 1, "colors"))) & 0x0F, TRUE);
605         }
606
607         return '1 1 1';
608 }
609
610 float getplayerisdead(float pl)
611 {
612         entity e;
613
614         e = CSQCModel_server2csqc(pl + 1);
615         if(e)
616                 return e.csqcmodel_isdead;
617
618         return FALSE;
619 }
620
621 void URI_Get_Callback(float id, float status, string data)
622 {
623         if(url_URI_Get_Callback(id, status, data))
624         {
625                 // handled
626         }
627         else if (id == URI_GET_DISCARD)
628         {
629                 // discard
630         }
631         else if (id >= URI_GET_CURL && id <= URI_GET_CURL_END)
632         {
633                 // sv_cmd curl
634                 Curl_URI_Get_Callback(id, status, data);
635         }
636         else
637         {
638                 printf("Received HTTP request data for an invalid id %d.\n", id);
639         }
640 }
641
642 void draw_beginBoldFont()
643 {
644         drawfont = FONT_USER+2;
645 }
646
647 void draw_endBoldFont()
648 {
649         drawfont = FONT_USER+1;
650 }
651
652
653 #define MAX_ACCURACY_LEVELS 10
654 float acc_lev[MAX_ACCURACY_LEVELS];
655 vector acc_col[MAX_ACCURACY_LEVELS];
656 float acc_col_loadtime;
657 float acc_levels;
658 string acc_color_levels;
659 void Accuracy_LoadLevels()
660 {
661         float i;
662         if(autocvar_accuracy_color_levels != acc_color_levels)
663         {
664                 if(acc_color_levels)
665                         strunzone(acc_color_levels);
666                 acc_color_levels = strzone(autocvar_accuracy_color_levels);
667                 acc_levels = tokenize_console(acc_color_levels);
668                 if(acc_levels > MAX_ACCURACY_LEVELS)
669                         acc_levels = MAX_ACCURACY_LEVELS;
670                 if(acc_levels < 2)
671                         print("Warning: accuracy_color_levels must contain at least 2 values\n");
672
673                 for(i = 0; i < acc_levels; ++i)
674                         acc_lev[i] = stof(argv(i)) / 100.0;
675         }
676 }
677
678 void Accuracy_LoadColors()
679 {
680         float i;
681         if(time > acc_col_loadtime)
682         if(acc_levels >= 2)
683         {
684                 for(i = 0; i < acc_levels; ++i)
685                         acc_col[i] = stov(cvar_string(strcat("accuracy_color", ftos(i))));
686                 acc_col_loadtime = time + 2;
687         }
688 }
689
690 vector Accuracy_GetColor(float accuracy)
691 {
692         float j, factor;
693         vector color;
694         if(acc_levels < 2)
695                 return '0 0 0'; // return black, can't determine the right color
696
697         // find the max level lower than acc
698         j = acc_levels-1;
699         while(j && accuracy < acc_lev[j])
700                 --j;
701
702         // inject color j+1 in color j, how much depending on how much accuracy is higher than level j
703         factor = (accuracy - acc_lev[j]) / (acc_lev[j+1] - acc_lev[j]);
704         color = acc_col[j];
705         color = color + factor * (acc_col[j+1] - color);
706         return color;
707 }
708