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