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