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