]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/client/waypointsprites.qc
34c1d08985dd93233ad95801eab5d0e3a04dbaf8
[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
10 #include "../common/buffs.qh"
11 #include "../common/constants.qh"
12 #include "../common/teams.qh"
13
14 #include "../common/weapons/weapons.qh"
15
16 #include "../csqcmodellib/interpolate.qh"
17
18 #include "../warpzonelib/mathlib.qh"
19
20 .float alpha;
21
22 void drawrotpic(vector org, float rot, string pic, vector sz, vector hotspot, vector rgb, float a, float f)
23 {
24         vector v1, v2, v3, v4;
25
26         hotspot = -1 * hotspot;
27
28         // hotspot-relative coordinates of the corners
29         v1 = hotspot;
30         v2 = hotspot + '1 0 0' * sz.x;
31         v3 = hotspot + '1 0 0' * sz.x + '0 1 0' * sz.y;
32         v4 = hotspot                  + '0 1 0' * sz.y;
33
34         // rotate them, and make them absolute
35         rot = -rot; // rotate by the opposite angle, as our coordinate system is reversed
36         v1 = rotate(v1, rot) + org;
37         v2 = rotate(v2, rot) + org;
38         v3 = rotate(v3, rot) + org;
39         v4 = rotate(v4, rot) + org;
40
41         // draw them
42         R_BeginPolygon(pic, f);
43         R_PolygonVertex(v1, '0 0 0', rgb, a);
44         R_PolygonVertex(v2, '1 0 0', rgb, a);
45         R_PolygonVertex(v3, '1 1 0', rgb, a);
46         R_PolygonVertex(v4, '0 1 0', rgb, a);
47         R_EndPolygon();
48 }
49
50 void drawquad(vector o, vector ri, vector up, string pic, vector rgb, float a, float f)
51 {
52         R_BeginPolygon(pic, f);
53         R_PolygonVertex(o, '0 0 0', rgb, a);
54         R_PolygonVertex(o + ri, '1 0 0', rgb, a);
55         R_PolygonVertex(o + up + ri, '1 1 0', rgb, a);
56         R_PolygonVertex(o + up, '0 1 0', rgb, a);
57         R_EndPolygon();
58 }
59
60 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)
61 {
62         vector o, ri, up;
63         float owidth; // outer width
64
65         hotspot = -1 * hotspot;
66
67         // hotspot-relative coordinates of the healthbar corners
68         o = hotspot;
69         ri = '1 0 0';
70         up = '0 1 0';
71
72         rot = -rot; // rotate by the opposite angle, as our coordinate system is reversed
73         o = rotate(o, rot) + org;
74         ri = rotate(ri, rot);
75         up = rotate(up, rot);
76
77         owidth = width + 2 * border;
78         o = o - up * (margin + border + height) + ri * (sz.x - owidth) * 0.5;
79
80         drawquad(o - up * border,                               ri * owidth,    up * border, "", rgb,  a,  f);
81         drawquad(o + up * height,                               ri * owidth,    up * border, "", rgb,  a,  f);
82         drawquad(o,                                             ri * border,    up * height, "", rgb,  a,  f);
83         drawquad(o + ri * (owidth - border),                    ri * border,    up * height, "", rgb,  a,  f);
84         drawquad(o + ri * (border + align * ((1 - h) * width)), ri * width * h, up * height, "", hrgb, ha, f);
85 }
86
87 // returns location of sprite text
88 vector drawspritearrow(vector o, float ang, vector rgb, float a, float t)
89 {
90         float size   = 9.0 * t;
91         float border = 1.5 * t;
92         float margin = 4.0 * t;
93
94         float borderDiag = border * 1.414;
95         vector arrowX  = eX * size;
96         vector arrowY  = eY * (size+borderDiag);
97         vector borderX = eX * (size+borderDiag);
98         vector borderY = eY * (size+borderDiag+border);
99
100         R_BeginPolygon("", DRAWFLAG_NORMAL);
101         R_PolygonVertex(o,                                  '0 0 0', '0 0 0', a);
102         R_PolygonVertex(o + rotate(arrowY  - borderX, ang), '0 0 0', '0 0 0', a);
103         R_PolygonVertex(o + rotate(borderY - borderX, ang), '0 0 0', '0 0 0', a);
104         R_PolygonVertex(o + rotate(borderY + borderX, ang), '0 0 0', '0 0 0', a);
105         R_PolygonVertex(o + rotate(arrowY  + borderX, ang), '0 0 0', '0 0 0', a);
106         R_EndPolygon();
107
108         R_BeginPolygon("", DRAWFLAG_ADDITIVE);
109         R_PolygonVertex(o + rotate(eY * borderDiag, ang), '0 0 0', rgb, a);
110         R_PolygonVertex(o + rotate(arrowY - arrowX, ang), '0 0 0', rgb, a);
111         R_PolygonVertex(o + rotate(arrowY + arrowX, ang), '0 0 0', rgb, a);
112         R_EndPolygon();
113
114         return o + rotate(eY * (borderDiag+size+margin), ang);
115 }
116
117 // returns location of sprite healthbar
118 vector drawspritetext(vector o, float ang, float minwidth, vector rgb, float a, vector fontsize, string s)
119 {
120         float algnx, algny;
121         float sw, w, h;
122         float aspect, sa, ca;
123
124         sw = stringwidth(s, false, fontsize);
125         if(sw > minwidth)
126                 w = sw;
127         else
128                 w = minwidth;
129         h = fontsize.y;
130
131         // how do corners work?
132         aspect = vid_conwidth / vid_conheight;
133         sa = sin(ang);
134         ca = cos(ang) * aspect;
135         if(fabs(sa) > fabs(ca))
136         {
137                 algnx = (sa < 0);
138                 algny = 0.5 - 0.5 * ca / fabs(sa);
139         }
140         else
141         {
142                 algnx = 0.5 - 0.5 * sa / fabs(ca);
143                 algny = (ca < 0);
144         }
145
146         // align
147         o.x -= w * algnx;
148         o.y -= h * algny;
149
150         // we want to be onscreen
151         if(o.x < 0)
152                 o.x = 0;
153         if(o.y < 0)
154                 o.y = 0;
155         if(o.x > vid_conwidth - w)
156                 o.x = vid_conwidth - w;
157         if(o.y > vid_conheight - h)
158                 o.x = vid_conheight - h;
159
160         o.x += 0.5 * (w - sw);
161
162         drawstring(o, s, fontsize, rgb, a, DRAWFLAG_NORMAL);
163
164         o.x += 0.5 * sw;
165         o.y += 0.5 * h;
166
167         return o;
168 }
169
170 float spritelookupblinkvalue(string s)
171 {
172         switch(s)
173         {
174                 case "ons-cp-atck-neut": return 2;
175                 case "ons-cp-atck-red":  return 2;
176                 case "ons-cp-atck-blue": return 2;
177                 case "ons-cp-dfnd-red":  return 0.5;
178                 case "ons-cp-dfnd-blue": return 0.5;
179                 case "item-invis":       return 2;
180                 case "item-extralife":   return 2;
181                 case "item-speed":       return 2;
182                 case "item-strength":    return 2;
183                 case "item-shield":      return 2;
184                 case "item-fuelregen":   return 2;
185                 case "item-jetpack":     return 2;
186                 case "tagged-target":    return 2;
187                 default:                 return 1;
188         }
189 }
190 vector spritelookupcolor(string s, vector def)
191 {
192         if(substring(s, 0, 4) == "wpn-")
193                 return (get_weaponinfo(stof(substring(s, 4, strlen(s)))).wpcolor);
194
195         switch(s)
196         {
197                 case "keycarrier-friend": return '0 1 0';
198                 default:                  return def;
199         }
200 }
201 string spritelookuptext(string s)
202 {
203         if(substring(s, 0, 4) == "wpn-") { return (get_weaponinfo(stof(substring(s, 4, strlen(s)))).message); }
204         if(substring(s, 0, 5) == "buff-") { return Buff_PrettyName(Buff_Type_FromSprite(s)); }
205
206         switch(s)
207         {
208                 case "as-push": return _("Push");
209                 case "as-destroy": return _("Destroy");
210                 case "as-defend": return _("Defend");
211                 case "bluebase": return _("Blue base");
212                 case "danger": return _("DANGER");
213                 case "enemyflagcarrier": return _("Enemy carrier");
214                 case "flagcarrier": return _("Flag carrier");
215                 case "flagdropped": return _("Dropped flag");
216                 case "helpme": return _("Help me!");
217                 case "here": return _("Here");
218                 case "key-dropped": return _("Dropped key");
219                 case "keycarrier-blue": return _("Key carrier");
220                 case "keycarrier-finish": return _("Run here");
221                 case "keycarrier-friend": return _("Key carrier");
222                 case "keycarrier-pink": return _("Key carrier");
223                 case "keycarrier-red": return _("Key carrier");
224                 case "keycarrier-yellow": return _("Key carrier");
225                 case "redbase": return _("Red 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 }