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