]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/client/waypointsprites.qc
Merge branch 'master' into Mario/vehicles
[xonotic/xonotic-data.pk3dir.git] / qcsrc / client / waypointsprites.qc
1 #include "waypointsprites.qh"
2
3 void drawrotpic(vector org, float rot, string pic, vector sz, vector hotspot, vector rgb, float a, float f)
4 {
5         vector v1, v2, v3, v4;
6
7         hotspot = -1 * hotspot;
8
9         // hotspot-relative coordinates of the corners
10         v1 = hotspot;
11         v2 = hotspot + '1 0 0' * sz.x;
12         v3 = hotspot + '1 0 0' * sz.x + '0 1 0' * sz.y;
13         v4 = hotspot                  + '0 1 0' * sz.y;
14
15         // rotate them, and make them absolute
16         rot = -rot; // rotate by the opposite angle, as our coordinate system is reversed
17         v1 = rotate(v1, rot) + org;
18         v2 = rotate(v2, rot) + org;
19         v3 = rotate(v3, rot) + org;
20         v4 = rotate(v4, rot) + org;
21
22         // draw them
23         R_BeginPolygon(pic, f);
24         R_PolygonVertex(v1, '0 0 0', rgb, a);
25         R_PolygonVertex(v2, '1 0 0', rgb, a);
26         R_PolygonVertex(v3, '1 1 0', rgb, a);
27         R_PolygonVertex(v4, '0 1 0', rgb, a);
28         R_EndPolygon();
29 }
30
31 void drawquad(vector o, vector ri, vector up, string pic, vector rgb, float a, float f)
32 {
33         R_BeginPolygon(pic, f);
34         R_PolygonVertex(o, '0 0 0', rgb, a);
35         R_PolygonVertex(o + ri, '1 0 0', rgb, a);
36         R_PolygonVertex(o + up + ri, '1 1 0', rgb, a);
37         R_PolygonVertex(o + up, '0 1 0', rgb, a);
38         R_EndPolygon();
39 }
40
41 void drawhealthbar(vector org, float rot, float h, vector sz, vector hotspot, float width, float height, float margin, float border, float align, vector rgb, float a, vector hrgb, float ha, float f)
42 {
43         vector o, ri, up;
44         float owidth; // outer width
45
46         hotspot = -1 * hotspot;
47
48         // hotspot-relative coordinates of the healthbar corners
49         o = hotspot;
50         ri = '1 0 0';
51         up = '0 1 0';
52
53         rot = -rot; // rotate by the opposite angle, as our coordinate system is reversed
54         o = rotate(o, rot) + org;
55         ri = rotate(ri, rot);
56         up = rotate(up, rot);
57
58         owidth = width + 2 * border;
59         o = o - up * (margin + border + height) + ri * (sz.x - owidth) * 0.5;
60
61         drawquad(o - up * border,                               ri * owidth,    up * border, "", rgb,  a,  f);
62         drawquad(o + up * height,                               ri * owidth,    up * border, "", rgb,  a,  f);
63         drawquad(o,                                             ri * border,    up * height, "", rgb,  a,  f);
64         drawquad(o + ri * (owidth - border),                    ri * border,    up * height, "", rgb,  a,  f);
65         drawquad(o + ri * (border + align * ((1 - h) * width)), ri * width * h, up * height, "", hrgb, ha, f);
66 }
67
68 // returns location of sprite text
69 vector drawspritearrow(vector o, float ang, vector rgb, float a, float t)
70 {
71         float size   = 9.0 * t;
72         float border = 1.5 * t;
73         float margin = 4.0 * t;
74
75         float borderDiag = border * 1.414;
76         vector arrowX  = eX * size;
77         vector arrowY  = eY * (size+borderDiag);
78         vector borderX = eX * (size+borderDiag);
79         vector borderY = eY * (size+borderDiag+border);
80
81         R_BeginPolygon("", DRAWFLAG_NORMAL);
82         R_PolygonVertex(o,                                  '0 0 0', '0 0 0', a);
83         R_PolygonVertex(o + rotate(arrowY  - borderX, ang), '0 0 0', '0 0 0', a);
84         R_PolygonVertex(o + rotate(borderY - borderX, ang), '0 0 0', '0 0 0', a);
85         R_PolygonVertex(o + rotate(borderY + borderX, ang), '0 0 0', '0 0 0', a);
86         R_PolygonVertex(o + rotate(arrowY  + borderX, ang), '0 0 0', '0 0 0', a);
87         R_EndPolygon();
88
89         R_BeginPolygon("", DRAWFLAG_ADDITIVE);
90         R_PolygonVertex(o + rotate(eY * borderDiag, ang), '0 0 0', rgb, a);
91         R_PolygonVertex(o + rotate(arrowY - arrowX, ang), '0 0 0', rgb, a);
92         R_PolygonVertex(o + rotate(arrowY + arrowX, ang), '0 0 0', rgb, a);
93         R_EndPolygon();
94
95         return o + rotate(eY * (borderDiag+size+margin), ang);
96 }
97
98 // returns location of sprite healthbar
99 vector drawspritetext(vector o, float ang, float minwidth, vector rgb, float a, vector fontsize, string s)
100 {
101         float algnx, algny;
102         float sw, w, h;
103         float aspect, sa, ca;
104
105         sw = stringwidth(s, false, fontsize);
106         if(sw > minwidth)
107                 w = sw;
108         else
109                 w = minwidth;
110         h = fontsize.y;
111
112         // how do corners work?
113         aspect = vid_conwidth / vid_conheight;
114         sa = sin(ang);
115         ca = cos(ang) * aspect;
116         if(fabs(sa) > fabs(ca))
117         {
118                 algnx = (sa < 0);
119                 algny = 0.5 - 0.5 * ca / fabs(sa);
120         }
121         else
122         {
123                 algnx = 0.5 - 0.5 * sa / fabs(ca);
124                 algny = (ca < 0);
125         }
126
127         // align
128         o.x -= w * algnx;
129         o.y -= h * algny;
130
131         // we want to be onscreen
132         if(o.x < 0)
133                 o.x = 0;
134         if(o.y < 0)
135                 o.y = 0;
136         if(o.x > vid_conwidth - w)
137                 o.x = vid_conwidth - w;
138         if(o.y > vid_conheight - h)
139                 o.x = vid_conheight - h;
140
141         o.x += 0.5 * (w - sw);
142
143         drawstring(o, s, fontsize, rgb, a, DRAWFLAG_NORMAL);
144
145         o.x += 0.5 * sw;
146         o.y += 0.5 * h;
147
148         return o;
149 }
150
151 float spritelookupblinkvalue(string s)
152 {
153         switch(s)
154         {
155                 case "ons-cp-atck-neut": return 2;
156                 case "ons-cp-atck-red":  return 2;
157                 case "ons-cp-atck-blue": return 2;
158                 case "ons-cp-dfnd-red":  return 0.5;
159                 case "ons-cp-dfnd-blue": return 0.5;
160                 case "item-invis":       return 2;
161                 case "item-extralife":   return 2;
162                 case "item-speed":       return 2;
163                 case "item-strength":    return 2;
164                 case "item-shield":      return 2;
165                 case "item-fuelregen":   return 2;
166                 case "item-jetpack":     return 2;
167                 case "tagged-target":    return 2;
168                 default:                 return 1;
169         }
170 }
171 vector spritelookupcolor(string s, vector def)
172 {
173         if(substring(s, 0, 4) == "wpn-")
174                 return (get_weaponinfo(stof(substring(s, 4, strlen(s)))).wpcolor);
175
176         switch(s)
177         {
178                 case "keycarrier-friend": return '0 1 0';
179                 default:                  return def;
180         }
181 }
182 string spritelookuptext(string s)
183 {
184         if(substring(s, 0, 4) == "wpn-") { return (get_weaponinfo(stof(substring(s, 4, strlen(s)))).message); }
185         if(substring(s, 0, 5) == "buff-") { return Buff_PrettyName(Buff_Type_FromSprite(s)); }
186
187         switch(s)
188         {
189                 case "as-push": return _("Push");
190                 case "as-destroy": return _("Destroy");
191                 case "as-defend": return _("Defend");
192                 case "bluebase": return _("Blue base");
193                 case "danger": return _("DANGER");
194                 case "enemyflagcarrier": return _("Enemy carrier");
195                 case "flagcarrier": return _("Flag carrier");
196                 case "flagdropped": return _("Dropped flag");
197                 case "helpme": return _("Help me!");
198                 case "here": return _("Here");
199                 case "key-dropped": return _("Dropped key");
200                 case "keycarrier-blue": return _("Key carrier");
201                 case "keycarrier-finish": return _("Run here");
202                 case "keycarrier-friend": return _("Key carrier");
203                 case "keycarrier-pink": return _("Key carrier");
204                 case "keycarrier-red": return _("Key carrier");
205                 case "keycarrier-yellow": return _("Key carrier");
206                 case "redbase": return _("Red base");
207                 case "waypoint": return _("Waypoint");
208                 case "ons-gen-red": return _("Generator");
209                 case "ons-gen-blue": return _("Generator");
210                 case "ons-gen-shielded": return _("Generator");
211                 case "ons-cp-neut": return _("Control point");
212                 case "ons-cp-red": return _("Control point");
213                 case "ons-cp-blue": return _("Control point");
214                 case "ons-cp-atck-neut": return _("Control point");
215                 case "ons-cp-atck-red": return _("Control point");
216                 case "ons-cp-atck-blue": return _("Control point");
217                 case "ons-cp-dfnd-red": return _("Control point");
218                 case "ons-cp-dfnd-blue": return _("Control point");
219                 case "race-checkpoint": return _("Checkpoint");
220                 case "race-finish": return _("Finish");
221                 case "race-start": return _("Start");
222                 case "race-start-finish": return (race_checkpointtime || race_mycheckpointtime) ? _("Finish") : _("Start");
223                 case "goal": return _("Goal");
224                 case "nb-ball": return _("Ball");
225                 case "ka-ball": return _("Ball");
226                 case "ka-ballcarrier": return _("Ball carrier");
227                 case "dom-neut": return _("Control point");
228                 case "dom-red": return _("Control point");
229                 case "dom-blue": return _("Control point");
230                 case "dom-yellow": return _("Control point");
231                 case "dom-pink": return _("Control point");
232                 case "item-invis": return _("Invisibility");
233                 case "item-extralife": return _("Extra life");
234                 case "item-speed": return _("Speed");
235                 case "item-strength": return _("Strength");
236                 case "item-shield": return _("Shield");
237                 case "item-fuelregen": return _("Fuel regen");
238                 case "item-jetpack": return _("Jet Pack");
239                 case "frozen": return _("Frozen!");
240                 case "tagged-target": return _("Tagged");
241                 case "vehicle": return _("Vehicle");
242                 case "intruder": return _("Intruder!");
243                 default: return s;
244         }
245 }
246
247 vector fixrgbexcess_move(vector rgb, vector src, vector dst)
248 {
249         vector yvec = '0.299 0.587 0.114';
250         return rgb + dst * ((src * yvec) / (dst * yvec)) * ((rgb - '1 1 1') * src);
251 }
252 vector fixrgbexcess(vector rgb)
253 {
254         if(rgb.x > 1)
255         {
256                 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 1 1');
257                 if(rgb.y > 1)
258                 {
259                         rgb = fixrgbexcess_move(rgb, '0 1 0', '0 0 1');
260                         if(rgb.z > 1)
261                                 rgb.z = 1;
262                 }
263                 else if(rgb.z > 1)
264                 {
265                         rgb = fixrgbexcess_move(rgb, '0 0 1', '0 1 0');
266                         if(rgb.y > 1)
267                                 rgb.y = 1;
268                 }
269         }
270         else if(rgb.y > 1)
271         {
272                 rgb = fixrgbexcess_move(rgb, '0 1 0', '1 0 1');
273                 if(rgb.x > 1)
274                 {
275                         rgb = fixrgbexcess_move(rgb, '1 0 0', '0 0 1');
276                         if(rgb.z > 1)
277                                 rgb.z = 1;
278                 }
279                 else if(rgb.z > 1)
280                 {
281                         rgb = fixrgbexcess_move(rgb, '0 0 1', '1 0 0');
282                         if(rgb.x > 1)
283                                 rgb.x = 1;
284                 }
285         }
286         else if(rgb.z > 1)
287         {
288                 rgb = fixrgbexcess_move(rgb, '0 0 1', '1 1 0');
289                 if(rgb.x > 1)
290                 {
291                         rgb = fixrgbexcess_move(rgb, '1 0 0', '0 1 0');
292                         if(rgb.y > 1)
293                                 rgb.y = 1;
294                 }
295                 else if(rgb.y > 1)
296                 {
297                         rgb = fixrgbexcess_move(rgb, '0 1 0', '1 0 0');
298                         if(rgb.x > 1)
299                                 rgb.x = 1;
300                 }
301         }
302         return rgb;
303 }
304
305 void Draw_WaypointSprite()
306 {
307         string spriteimage;
308         float t;
309
310         if(self.lifetime)
311                 self.alpha = pow(bound(0, (self.fadetime - time) / self.lifetime, 1), waypointsprite_timealphaexponent);
312         else
313                 self.alpha = 1;
314
315         if(self.hideflags & 2)
316                 return; // radar only
317
318         if(autocvar_cl_hidewaypoints >= 2)
319                 return;
320
321         if(self.hideflags & 1)
322                 if(autocvar_cl_hidewaypoints)
323                         return; // fixed waypoint
324
325         InterpolateOrigin_Do();
326
327         t = GetPlayerColor(player_localnum) + 1;
328
329         spriteimage = "";
330
331         // choose the sprite
332         switch(self.rule)
333         {
334                 case SPRITERULE_DEFAULT:
335                         if(self.team)
336                         {
337                                 if(self.team == t)
338                                         spriteimage = self.netname;
339                                 else
340                                         spriteimage = "";
341                         }
342                         else
343                                 spriteimage = self.netname;
344                         break;
345                 case SPRITERULE_TEAMPLAY:
346                         if(t == NUM_SPECTATOR + 1)
347                                 spriteimage = self.netname3;
348                         else if(self.team == t)
349                                 spriteimage = self.netname2;
350                         else
351                                 spriteimage = self.netname;
352                         break;
353                 default:
354                         error("Invalid waypointsprite rule!");
355                         break;
356         }
357
358         if(spriteimage == "")
359                 return;
360
361         ++waypointsprite_newcount;
362
363         float dist;
364         dist = vlen(self.origin - view_origin);
365
366         float a;
367         a = self.alpha * autocvar_hud_panel_fg_alpha;
368
369         if(self.maxdistance > waypointsprite_normdistance)
370                 a *= pow(bound(0, (self.maxdistance - dist) / (self.maxdistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent);
371         else if(self.maxdistance > 0)
372                 a *= pow(bound(0, (waypointsprite_fadedistance - dist) / (waypointsprite_fadedistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent) * (1 - waypointsprite_minalpha) + waypointsprite_minalpha;
373
374         vector rgb;
375         rgb = self.teamradar_color;
376         rgb = spritelookupcolor(spriteimage, rgb);
377         if(rgb == '0 0 0')
378         {
379                 self.teamradar_color = '1 0 1';
380                 printf("WARNING: sprite of name %s has no color, using pink so you notice it\n", spriteimage);
381         }
382
383         if(time - floor(time) > 0.5)
384         {
385                 if(self.helpme && time < self.helpme)
386                         a *= SPRITE_HELPME_BLINK;
387                 else
388                         a *= spritelookupblinkvalue(spriteimage);
389         }
390
391         if(a > 1)
392         {
393                 rgb *= a;
394                 a = 1;
395         }
396
397         if(a <= 0)
398                 return;
399
400         rgb = fixrgbexcess(rgb);
401
402         vector o;
403         float ang;
404
405         o = project_3d_to_2d(self.origin);
406         if(o.z < 0
407         || o.x < (vid_conwidth * waypointsprite_edgeoffset_left)
408         || o.y < (vid_conheight * waypointsprite_edgeoffset_top)
409         || o.x > (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right))
410         || o.y > (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom)))
411         {
412                 // scale it to be just in view
413                 vector d;
414                 float f1, f2;
415
416                 d = o - '0.5 0 0' * vid_conwidth - '0 0.5 0' * vid_conheight;
417                 ang = atan2(-d.x, -d.y);
418                 if(o.z < 0)
419                         ang += M_PI;
420
421                 f1 = d.x / vid_conwidth;
422                 f2 = d.y / vid_conheight;
423
424                 if(max(f1, -f1) > max(f2, -f2))
425                 {
426                         if(d.z * f1 > 0)
427                         {
428                                 // RIGHT edge
429                                 d = d * ((0.5 - waypointsprite_edgeoffset_right) / f1);
430                         }
431                         else
432                         {
433                                 // LEFT edge
434                                 d = d * (-(0.5 - waypointsprite_edgeoffset_left) / f1);
435                         }
436                 }
437                 else
438                 {
439                         if(d.z * f2 > 0)
440                         {
441                                 // BOTTOM edge
442                                 d = d * ((0.5 - waypointsprite_edgeoffset_bottom) / f2);
443                         }
444                         else
445                         {
446                                 // TOP edge
447                                 d = d * (-(0.5 - waypointsprite_edgeoffset_top) / f2);
448                         }
449                 }
450
451                 o = d + '0.5 0 0' * vid_conwidth + '0 0.5 0' * vid_conheight;
452         }
453         else
454         {
455 #if 1
456                 ang = M_PI;
457 #else
458                 vector d;
459                 d = o - '0.5 0 0' * vid_conwidth - '0 0.5 0' * vid_conheight;
460                 ang = atan2(-d.x, -d.y);
461 #endif
462         }
463         o.z = 0;
464
465         float edgedistance_min, crosshairdistance;
466                 edgedistance_min = min((o.y - (vid_conheight * waypointsprite_edgeoffset_top)),
467         (o.x - (vid_conwidth * waypointsprite_edgeoffset_left)),
468         (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right)) - o.x,
469         (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom)) - o.y);
470
471         float vidscale;
472         vidscale = max(vid_conwidth / vid_width, vid_conheight / vid_height);
473
474         crosshairdistance = sqrt( pow(o.x - vid_conwidth/2, 2) + pow(o.y - vid_conheight/2, 2) );
475
476         t = waypointsprite_scale * vidscale;
477         a *= waypointsprite_alpha;
478
479         {
480                 a = a * (1 - (1 - waypointsprite_distancefadealpha) * (bound(0, dist/waypointsprite_distancefadedistance, 1)));
481                 t = t * (1 - (1 - waypointsprite_distancefadescale) * (bound(0, dist/waypointsprite_distancefadedistance, 1)));
482         }
483         if (edgedistance_min < waypointsprite_edgefadedistance) {
484                 a = a * (1 - (1 - waypointsprite_edgefadealpha) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1)));
485                 t = t * (1 - (1 - waypointsprite_edgefadescale) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1)));
486         }
487         if(crosshairdistance < waypointsprite_crosshairfadedistance) {
488                 a = a * (1 - (1 - waypointsprite_crosshairfadealpha) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1)));
489                 t = t * (1 - (1 - waypointsprite_crosshairfadescale) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1)));
490         }
491
492         if(self.build_finished)
493         {
494                 if(time < self.build_finished + 0.25)
495                 {
496                         if(time < self.build_started)
497                                 self.health = self.build_starthealth;
498                         else if(time < self.build_finished)
499                                 self.health = (time - self.build_started) / (self.build_finished - self.build_started) * (1 - self.build_starthealth) + self.build_starthealth;
500                         else
501                                 self.health = 1;
502                 }
503                 else
504                         self.health = -1;
505         }
506
507         o = drawspritearrow(o, ang, rgb, a, SPRITE_ARROW_SCALE * t);
508
509         string txt;
510         if(autocvar_g_waypointsprite_spam && waypointsprite_count >= autocvar_g_waypointsprite_spam)
511                 txt = _("Spam");
512         else
513                 txt = spritelookuptext(spriteimage);
514         if(self.helpme && time < self.helpme)
515                 txt = sprintf(_("%s needing help!"), txt);
516         if(autocvar_g_waypointsprite_uppercase)
517                 txt = strtoupper(txt);
518
519         draw_beginBoldFont();
520         if(self.health >= 0)
521         {
522                 o = drawspritetext(o, ang, (SPRITE_HEALTHBAR_WIDTH + 2 * SPRITE_HEALTHBAR_BORDER) * t, rgb, a, waypointsprite_fontsize * '1 1 0', txt);
523
524                 float align, marg;
525                 if(self.build_finished)
526                         align = 0.5;
527                 else
528                         align = 0;
529                 if(cos(ang) > 0)
530                         marg = -(SPRITE_HEALTHBAR_MARGIN + SPRITE_HEALTHBAR_HEIGHT + 2 * SPRITE_HEALTHBAR_BORDER) * t - 0.5 * waypointsprite_fontsize;
531                 else
532                         marg = SPRITE_HEALTHBAR_MARGIN * t + 0.5 * waypointsprite_fontsize;
533                 drawhealthbar(
534                                 o,
535                                 0,
536                                 self.health,
537                                 '0 0 0',
538                                 '0 0 0',
539                                 SPRITE_HEALTHBAR_WIDTH * t,
540                                 SPRITE_HEALTHBAR_HEIGHT * t,
541                                 marg,
542                                 SPRITE_HEALTHBAR_BORDER * t,
543                                 align,
544                                 rgb,
545                                 a * SPRITE_HEALTHBAR_BORDERALPHA,
546                                 rgb,
547                                 a * SPRITE_HEALTHBAR_HEALTHALPHA,
548                                 DRAWFLAG_NORMAL
549                              );
550         }
551         else
552         {
553                 o = drawspritetext(o, ang, 0, rgb, a, waypointsprite_fontsize * '1 1 0', txt);
554         }
555         draw_endBoldFont();
556 }
557
558 void Ent_RemoveWaypointSprite()
559 {
560         if(self.netname)
561                 strunzone(self.netname);
562         if(self.netname2)
563                 strunzone(self.netname2);
564         if(self.netname3)
565                 strunzone(self.netname3);
566 }
567
568 void Ent_WaypointSprite()
569 {
570         int sendflags, f, t;
571         sendflags = ReadByte();
572
573         if(!self.spawntime)
574                 self.spawntime = time;
575
576         self.draw2d = Draw_WaypointSprite;
577
578         InterpolateOrigin_Undo();
579         self.iflags |= IFLAG_ORIGIN;
580
581         if(sendflags & 0x80)
582         {
583                 t = ReadByte();
584                 if(t < 192)
585                 {
586                         self.health = t / 191.0;
587                         self.build_finished = 0;
588                 }
589                 else
590                 {
591                         t = (t - 192) * 256 + ReadByte();
592                         self.build_started = servertime;
593                         if(self.build_finished)
594                                 self.build_starthealth = bound(0, self.health, 1);
595                         else
596                                 self.build_starthealth = 0;
597                         self.build_finished = servertime + t / 32;
598                 }
599         }
600         else
601         {
602                 self.health = -1;
603                 self.build_finished = 0;
604         }
605
606         if(sendflags & 64)
607         {
608                 // unfortunately, this needs to be exact (for the 3D display)
609                 self.origin_x = ReadCoord();
610                 self.origin_y = ReadCoord();
611                 self.origin_z = ReadCoord();
612                 setorigin(self, self.origin);
613         }
614
615         if(sendflags & 1)
616         {
617                 self.team = ReadByte();
618                 self.rule = ReadByte();
619         }
620
621         if(sendflags & 2)
622         {
623                 if(self.netname)
624                         strunzone(self.netname);
625                 self.netname = strzone(ReadString());
626         }
627
628         if(sendflags & 4)
629         {
630                 if(self.netname2)
631                         strunzone(self.netname2);
632                 self.netname2 = strzone(ReadString());
633         }
634
635         if(sendflags & 8)
636         {
637                 if(self.netname3)
638                         strunzone(self.netname3);
639                 self.netname3 = strzone(ReadString());
640         }
641
642         if(sendflags & 16)
643         {
644                 self.lifetime = ReadCoord();
645                 self.fadetime = ReadCoord();
646                 self.maxdistance = ReadShort();
647                 self.hideflags = ReadByte();
648         }
649
650         if(sendflags & 32)
651         {
652                 f = ReadByte();
653                 self.teamradar_icon = (f & 0x7F);
654                 if(f & 0x80)
655                 {
656                         self.(teamradar_times[self.teamradar_time_index]) = time;
657                         self.teamradar_time_index = (self.teamradar_time_index + 1) % MAX_TEAMRADAR_TIMES;
658                 }
659                 self.teamradar_color_x = ReadByte() / 255.0;
660                 self.teamradar_color_y = ReadByte() / 255.0;
661                 self.teamradar_color_z = ReadByte() / 255.0;
662                 self.helpme = ReadByte() * 0.1;
663                 if(self.helpme > 0)
664                         self.helpme += servertime;
665         }
666
667         InterpolateOrigin_Note();
668
669         self.entremove = Ent_RemoveWaypointSprite;
670 }
671
672 void WaypointSprite_Load_Frames(string ext)
673 {
674         float dh, n, i, o, f;
675         string s, sname, sframes;
676         dh = search_begin(strcat("models/sprites/*_frame*", ext), false, false);
677         if (dh < 0)
678                  return;
679         float ext_len = strlen(ext);
680         n = search_getsize(dh);
681         for(i = 0; i < n; ++i)
682         {
683                 s = search_getfilename(dh, i);
684                 s = substring(s, 15, strlen(s) - 15 - ext_len); // strip models/sprites/ and extension
685
686                 o = strstrofs(s, "_frame", 0);
687                 sname = strcat("/spriteframes/", substring(s, 0, o));
688                 sframes = substring(s, o + 6, strlen(s) - o - 6);
689                 f = stof(sframes) + 1;
690                 db_put(tempdb, sname, ftos(max(f, stof(db_get(tempdb, sname)))));
691         }
692         search_end(dh);
693 }
694
695 void WaypointSprite_Load()
696 {
697         waypointsprite_fadedistance = vlen(mi_scale);
698         waypointsprite_normdistance = autocvar_g_waypointsprite_normdistance;
699         waypointsprite_minscale = autocvar_g_waypointsprite_minscale;
700         waypointsprite_minalpha = autocvar_g_waypointsprite_minalpha;
701         waypointsprite_distancealphaexponent = autocvar_g_waypointsprite_distancealphaexponent;
702         waypointsprite_timealphaexponent = autocvar_g_waypointsprite_timealphaexponent;
703         waypointsprite_scale = autocvar_g_waypointsprite_scale;
704         waypointsprite_fontsize = autocvar_g_waypointsprite_fontsize;
705         waypointsprite_edgefadealpha = autocvar_g_waypointsprite_edgefadealpha;
706         waypointsprite_edgefadescale = autocvar_g_waypointsprite_edgefadescale;
707         waypointsprite_edgefadedistance = autocvar_g_waypointsprite_edgefadedistance;
708         waypointsprite_edgeoffset_bottom = autocvar_g_waypointsprite_edgeoffset_bottom;
709         waypointsprite_edgeoffset_left = autocvar_g_waypointsprite_edgeoffset_left;
710         waypointsprite_edgeoffset_right = autocvar_g_waypointsprite_edgeoffset_right;
711         waypointsprite_edgeoffset_top = autocvar_g_waypointsprite_edgeoffset_top;
712         waypointsprite_crosshairfadealpha = autocvar_g_waypointsprite_crosshairfadealpha;
713         waypointsprite_crosshairfadescale = autocvar_g_waypointsprite_crosshairfadescale;
714         waypointsprite_crosshairfadedistance = autocvar_g_waypointsprite_crosshairfadedistance;
715         waypointsprite_distancefadealpha = autocvar_g_waypointsprite_distancefadealpha;
716         waypointsprite_distancefadescale = autocvar_g_waypointsprite_distancefadescale;
717         waypointsprite_distancefadedistance = waypointsprite_fadedistance * autocvar_g_waypointsprite_distancefadedistancemultiplier;
718         waypointsprite_alpha = autocvar_g_waypointsprite_alpha * (1 - autocvar__menu_alpha);
719
720         if(!waypointsprite_initialized)
721         {
722                 WaypointSprite_Load_Frames(".tga");
723                 WaypointSprite_Load_Frames(".jpg");
724                 waypointsprite_initialized = true;
725         }
726
727         waypointsprite_count = waypointsprite_newcount;
728         waypointsprite_newcount = 0;
729 }