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