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