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