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