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