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