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