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