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