]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/client/waypointsprites.qc
Merge branch 'master' into Mario/vaporizer_damage
[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                 default: return s;
243         }
244 }
245
246 vector fixrgbexcess_move(vector rgb, vector src, vector dst)
247 {
248         vector yvec = '0.299 0.587 0.114';
249         return rgb + dst * ((src * yvec) / (dst * yvec)) * ((rgb - '1 1 1') * src);
250 }
251 vector fixrgbexcess(vector rgb)
252 {
253         if(rgb.x > 1)
254         {
255                 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 1 1');
256                 if(rgb.y > 1)
257                 {
258                         rgb = fixrgbexcess_move(rgb, '0 1 0', '0 0 1');
259                         if(rgb.z > 1)
260                                 rgb.z = 1;
261                 }
262                 else if(rgb.z > 1)
263                 {
264                         rgb = fixrgbexcess_move(rgb, '0 0 1', '0 1 0');
265                         if(rgb.y > 1)
266                                 rgb.y = 1;
267                 }
268         }
269         else if(rgb.y > 1)
270         {
271                 rgb = fixrgbexcess_move(rgb, '0 1 0', '1 0 1');
272                 if(rgb.x > 1)
273                 {
274                         rgb = fixrgbexcess_move(rgb, '1 0 0', '0 0 1');
275                         if(rgb.z > 1)
276                                 rgb.z = 1;
277                 }
278                 else if(rgb.z > 1)
279                 {
280                         rgb = fixrgbexcess_move(rgb, '0 0 1', '1 0 0');
281                         if(rgb.x > 1)
282                                 rgb.x = 1;
283                 }
284         }
285         else if(rgb.z > 1)
286         {
287                 rgb = fixrgbexcess_move(rgb, '0 0 1', '1 1 0');
288                 if(rgb.x > 1)
289                 {
290                         rgb = fixrgbexcess_move(rgb, '1 0 0', '0 1 0');
291                         if(rgb.y > 1)
292                                 rgb.y = 1;
293                 }
294                 else if(rgb.y > 1)
295                 {
296                         rgb = fixrgbexcess_move(rgb, '0 1 0', '1 0 0');
297                         if(rgb.x > 1)
298                                 rgb.x = 1;
299                 }
300         }
301         return rgb;
302 }
303
304 void Draw_WaypointSprite()
305 {
306         string spriteimage;
307         float t;
308
309         if(self.lifetime)
310                 self.alpha = pow(bound(0, (self.fadetime - time) / self.lifetime, 1), waypointsprite_timealphaexponent);
311         else
312                 self.alpha = 1;
313
314         if(self.hideflags & 2)
315                 return; // radar only
316
317         if(autocvar_cl_hidewaypoints >= 2)
318                 return;
319
320         if(self.hideflags & 1)
321                 if(autocvar_cl_hidewaypoints)
322                         return; // fixed waypoint
323
324         InterpolateOrigin_Do();
325
326         t = GetPlayerColor(player_localnum) + 1;
327
328         spriteimage = "";
329
330         // choose the sprite
331         switch(self.rule)
332         {
333                 case SPRITERULE_DEFAULT:
334                         if(self.team)
335                         {
336                                 if(self.team == t)
337                                         spriteimage = self.netname;
338                                 else
339                                         spriteimage = "";
340                         }
341                         else
342                                 spriteimage = self.netname;
343                         break;
344                 case SPRITERULE_TEAMPLAY:
345                         if(t == NUM_SPECTATOR + 1)
346                                 spriteimage = self.netname3;
347                         else if(self.team == t)
348                                 spriteimage = self.netname2;
349                         else
350                                 spriteimage = self.netname;
351                         break;
352                 default:
353                         error("Invalid waypointsprite rule!");
354                         break;
355         }
356
357         if(spriteimage == "")
358                 return;
359
360         ++waypointsprite_newcount;
361
362         float dist;
363         dist = vlen(self.origin - view_origin);
364
365         float a;
366         a = self.alpha * autocvar_hud_panel_fg_alpha;
367
368         if(self.maxdistance > waypointsprite_normdistance)
369                 a *= pow(bound(0, (self.maxdistance - dist) / (self.maxdistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent);
370         else if(self.maxdistance > 0)
371                 a *= pow(bound(0, (waypointsprite_fadedistance - dist) / (waypointsprite_fadedistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent) * (1 - waypointsprite_minalpha) + waypointsprite_minalpha;
372
373         vector rgb;
374         rgb = self.teamradar_color;
375         rgb = spritelookupcolor(spriteimage, rgb);
376         if(rgb == '0 0 0')
377         {
378                 self.teamradar_color = '1 0 1';
379                 printf("WARNING: sprite of name %s has no color, using pink so you notice it\n", spriteimage);
380         }
381
382         if(time - floor(time) > 0.5)
383         {
384                 if(self.helpme && time < self.helpme)
385                         a *= SPRITE_HELPME_BLINK;
386                 else
387                         a *= spritelookupblinkvalue(spriteimage);
388         }
389
390         if(a > 1)
391         {
392                 rgb *= a;
393                 a = 1;
394         }
395
396         if(a <= 0)
397                 return;
398
399         rgb = fixrgbexcess(rgb);
400
401         vector o;
402         float ang;
403
404         o = project_3d_to_2d(self.origin);
405         if(o.z < 0
406         || o.x < (vid_conwidth * waypointsprite_edgeoffset_left)
407         || o.y < (vid_conheight * waypointsprite_edgeoffset_top)
408         || o.x > (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right))
409         || o.y > (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom)))
410         {
411                 // scale it to be just in view
412                 vector d;
413                 float f1, f2;
414
415                 d = o - '0.5 0 0' * vid_conwidth - '0 0.5 0' * vid_conheight;
416                 ang = atan2(-d.x, -d.y);
417                 if(o.z < 0)
418                         ang += M_PI;
419
420                 f1 = d.x / vid_conwidth;
421                 f2 = d.y / vid_conheight;
422
423                 if(max(f1, -f1) > max(f2, -f2))
424                 {
425                         if(d.z * f1 > 0)
426                         {
427                                 // RIGHT edge
428                                 d = d * ((0.5 - waypointsprite_edgeoffset_right) / f1);
429                         }
430                         else
431                         {
432                                 // LEFT edge
433                                 d = d * (-(0.5 - waypointsprite_edgeoffset_left) / f1);
434                         }
435                 }
436                 else
437                 {
438                         if(d.z * f2 > 0)
439                         {
440                                 // BOTTOM edge
441                                 d = d * ((0.5 - waypointsprite_edgeoffset_bottom) / f2);
442                         }
443                         else
444                         {
445                                 // TOP edge
446                                 d = d * (-(0.5 - waypointsprite_edgeoffset_top) / f2);
447                         }
448                 }
449
450                 o = d + '0.5 0 0' * vid_conwidth + '0 0.5 0' * vid_conheight;
451         }
452         else
453         {
454 #if 1
455                 ang = M_PI;
456 #else
457                 vector d;
458                 d = o - '0.5 0 0' * vid_conwidth - '0 0.5 0' * vid_conheight;
459                 ang = atan2(-d.x, -d.y);
460 #endif
461         }
462         o.z = 0;
463
464         float edgedistance_min, crosshairdistance;
465                 edgedistance_min = min((o.y - (vid_conheight * waypointsprite_edgeoffset_top)),
466         (o.x - (vid_conwidth * waypointsprite_edgeoffset_left)),
467         (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right)) - o.x,
468         (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom)) - o.y);
469
470         float vidscale;
471         vidscale = max(vid_conwidth / vid_width, vid_conheight / vid_height);
472
473         crosshairdistance = sqrt( pow(o.x - vid_conwidth/2, 2) + pow(o.y - vid_conheight/2, 2) );
474
475         t = waypointsprite_scale * vidscale;
476         a *= waypointsprite_alpha;
477
478         {
479                 a = a * (1 - (1 - waypointsprite_distancefadealpha) * (bound(0, dist/waypointsprite_distancefadedistance, 1)));
480                 t = t * (1 - (1 - waypointsprite_distancefadescale) * (bound(0, dist/waypointsprite_distancefadedistance, 1)));
481         }
482         if (edgedistance_min < waypointsprite_edgefadedistance) {
483                 a = a * (1 - (1 - waypointsprite_edgefadealpha) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1)));
484                 t = t * (1 - (1 - waypointsprite_edgefadescale) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1)));
485         }
486         if(crosshairdistance < waypointsprite_crosshairfadedistance) {
487                 a = a * (1 - (1 - waypointsprite_crosshairfadealpha) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1)));
488                 t = t * (1 - (1 - waypointsprite_crosshairfadescale) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1)));
489         }
490
491         if(self.build_finished)
492         {
493                 if(time < self.build_finished + 0.25)
494                 {
495                         if(time < self.build_started)
496                                 self.health = self.build_starthealth;
497                         else if(time < self.build_finished)
498                                 self.health = (time - self.build_started) / (self.build_finished - self.build_started) * (1 - self.build_starthealth) + self.build_starthealth;
499                         else
500                                 self.health = 1;
501                 }
502                 else
503                         self.health = -1;
504         }
505
506         o = drawspritearrow(o, ang, rgb, a, SPRITE_ARROW_SCALE * t);
507
508         string txt;
509         if(autocvar_g_waypointsprite_spam && waypointsprite_count >= autocvar_g_waypointsprite_spam)
510                 txt = _("Spam");
511         else
512                 txt = spritelookuptext(spriteimage);
513         if(self.helpme && time < self.helpme)
514                 txt = sprintf(_("%s needing help!"), txt);
515         if(autocvar_g_waypointsprite_uppercase)
516                 txt = strtoupper(txt);
517
518         draw_beginBoldFont();
519         if(self.health >= 0)
520         {
521                 o = drawspritetext(o, ang, (SPRITE_HEALTHBAR_WIDTH + 2 * SPRITE_HEALTHBAR_BORDER) * t, rgb, a, waypointsprite_fontsize * '1 1 0', txt);
522
523                 float align, marg;
524                 if(self.build_finished)
525                         align = 0.5;
526                 else
527                         align = 0;
528                 if(cos(ang) > 0)
529                         marg = -(SPRITE_HEALTHBAR_MARGIN + SPRITE_HEALTHBAR_HEIGHT + 2 * SPRITE_HEALTHBAR_BORDER) * t - 0.5 * waypointsprite_fontsize;
530                 else
531                         marg = SPRITE_HEALTHBAR_MARGIN * t + 0.5 * waypointsprite_fontsize;
532                 drawhealthbar(
533                                 o,
534                                 0,
535                                 self.health,
536                                 '0 0 0',
537                                 '0 0 0',
538                                 SPRITE_HEALTHBAR_WIDTH * t,
539                                 SPRITE_HEALTHBAR_HEIGHT * t,
540                                 marg,
541                                 SPRITE_HEALTHBAR_BORDER * t,
542                                 align,
543                                 rgb,
544                                 a * SPRITE_HEALTHBAR_BORDERALPHA,
545                                 rgb,
546                                 a * SPRITE_HEALTHBAR_HEALTHALPHA,
547                                 DRAWFLAG_NORMAL
548                              );
549         }
550         else
551         {
552                 o = drawspritetext(o, ang, 0, rgb, a, waypointsprite_fontsize * '1 1 0', txt);
553         }
554         draw_endBoldFont();
555 }
556
557 void Ent_RemoveWaypointSprite()
558 {
559         if(self.netname)
560                 strunzone(self.netname);
561         if(self.netname2)
562                 strunzone(self.netname2);
563         if(self.netname3)
564                 strunzone(self.netname3);
565 }
566
567 void Ent_WaypointSprite()
568 {
569         int sendflags, f, t;
570         sendflags = ReadByte();
571
572         if(!self.spawntime)
573                 self.spawntime = time;
574
575         self.draw2d = Draw_WaypointSprite;
576
577         InterpolateOrigin_Undo();
578         self.iflags |= IFLAG_ORIGIN;
579
580         if(sendflags & 0x80)
581         {
582                 t = ReadByte();
583                 if(t < 192)
584                 {
585                         self.health = t / 191.0;
586                         self.build_finished = 0;
587                 }
588                 else
589                 {
590                         t = (t - 192) * 256 + ReadByte();
591                         self.build_started = servertime;
592                         if(self.build_finished)
593                                 self.build_starthealth = bound(0, self.health, 1);
594                         else
595                                 self.build_starthealth = 0;
596                         self.build_finished = servertime + t / 32;
597                 }
598         }
599         else
600         {
601                 self.health = -1;
602                 self.build_finished = 0;
603         }
604
605         if(sendflags & 64)
606         {
607                 // unfortunately, this needs to be exact (for the 3D display)
608                 self.origin_x = ReadCoord();
609                 self.origin_y = ReadCoord();
610                 self.origin_z = ReadCoord();
611                 setorigin(self, self.origin);
612         }
613
614         if(sendflags & 1)
615         {
616                 self.team = ReadByte();
617                 self.rule = ReadByte();
618         }
619
620         if(sendflags & 2)
621         {
622                 if(self.netname)
623                         strunzone(self.netname);
624                 self.netname = strzone(ReadString());
625         }
626
627         if(sendflags & 4)
628         {
629                 if(self.netname2)
630                         strunzone(self.netname2);
631                 self.netname2 = strzone(ReadString());
632         }
633
634         if(sendflags & 8)
635         {
636                 if(self.netname3)
637                         strunzone(self.netname3);
638                 self.netname3 = strzone(ReadString());
639         }
640
641         if(sendflags & 16)
642         {
643                 self.lifetime = ReadCoord();
644                 self.fadetime = ReadCoord();
645                 self.maxdistance = ReadShort();
646                 self.hideflags = ReadByte();
647         }
648
649         if(sendflags & 32)
650         {
651                 f = ReadByte();
652                 self.teamradar_icon = (f & 0x7F);
653                 if(f & 0x80)
654                 {
655                         self.(teamradar_times[self.teamradar_time_index]) = time;
656                         self.teamradar_time_index = (self.teamradar_time_index + 1) % MAX_TEAMRADAR_TIMES;
657                 }
658                 self.teamradar_color_x = ReadByte() / 255.0;
659                 self.teamradar_color_y = ReadByte() / 255.0;
660                 self.teamradar_color_z = ReadByte() / 255.0;
661                 self.helpme = ReadByte() * 0.1;
662                 if(self.helpme > 0)
663                         self.helpme += servertime;
664         }
665
666         InterpolateOrigin_Note();
667
668         self.entremove = Ent_RemoveWaypointSprite;
669 }
670
671 void WaypointSprite_Load_Frames(string ext)
672 {
673         float dh, n, i, o, f;
674         string s, sname, sframes;
675         dh = search_begin(strcat("models/sprites/*_frame*", ext), false, false);
676         if (dh < 0)
677                  return;
678         float ext_len = strlen(ext);
679         n = search_getsize(dh);
680         for(i = 0; i < n; ++i)
681         {
682                 s = search_getfilename(dh, i);
683                 s = substring(s, 15, strlen(s) - 15 - ext_len); // strip models/sprites/ and extension
684
685                 o = strstrofs(s, "_frame", 0);
686                 sname = strcat("/spriteframes/", substring(s, 0, o));
687                 sframes = substring(s, o + 6, strlen(s) - o - 6);
688                 f = stof(sframes) + 1;
689                 db_put(tempdb, sname, ftos(max(f, stof(db_get(tempdb, sname)))));
690         }
691         search_end(dh);
692 }
693
694 void WaypointSprite_Load()
695 {
696         waypointsprite_fadedistance = vlen(mi_scale);
697         waypointsprite_normdistance = autocvar_g_waypointsprite_normdistance;
698         waypointsprite_minscale = autocvar_g_waypointsprite_minscale;
699         waypointsprite_minalpha = autocvar_g_waypointsprite_minalpha;
700         waypointsprite_distancealphaexponent = autocvar_g_waypointsprite_distancealphaexponent;
701         waypointsprite_timealphaexponent = autocvar_g_waypointsprite_timealphaexponent;
702         waypointsprite_scale = autocvar_g_waypointsprite_scale;
703         waypointsprite_fontsize = autocvar_g_waypointsprite_fontsize;
704         waypointsprite_edgefadealpha = autocvar_g_waypointsprite_edgefadealpha;
705         waypointsprite_edgefadescale = autocvar_g_waypointsprite_edgefadescale;
706         waypointsprite_edgefadedistance = autocvar_g_waypointsprite_edgefadedistance;
707         waypointsprite_edgeoffset_bottom = autocvar_g_waypointsprite_edgeoffset_bottom;
708         waypointsprite_edgeoffset_left = autocvar_g_waypointsprite_edgeoffset_left;
709         waypointsprite_edgeoffset_right = autocvar_g_waypointsprite_edgeoffset_right;
710         waypointsprite_edgeoffset_top = autocvar_g_waypointsprite_edgeoffset_top;
711         waypointsprite_crosshairfadealpha = autocvar_g_waypointsprite_crosshairfadealpha;
712         waypointsprite_crosshairfadescale = autocvar_g_waypointsprite_crosshairfadescale;
713         waypointsprite_crosshairfadedistance = autocvar_g_waypointsprite_crosshairfadedistance;
714         waypointsprite_distancefadealpha = autocvar_g_waypointsprite_distancefadealpha;
715         waypointsprite_distancefadescale = autocvar_g_waypointsprite_distancefadescale;
716         waypointsprite_distancefadedistance = waypointsprite_fadedistance * autocvar_g_waypointsprite_distancefadedistancemultiplier;
717         waypointsprite_alpha = autocvar_g_waypointsprite_alpha * (1 - autocvar__menu_alpha);
718
719         if(!waypointsprite_initialized)
720         {
721                 WaypointSprite_Load_Frames(".tga");
722                 WaypointSprite_Load_Frames(".jpg");
723                 waypointsprite_initialized = true;
724         }
725
726         waypointsprite_count = waypointsprite_newcount;
727         waypointsprite_newcount = 0;
728 }