]> 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 #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         switch(s)
170         {
171                 case "ons-cp-atck":      return 2;
172                 case "ons-cp-dfnd":      return 0.5;
173                 case "item-invis":       return 2;
174                 case "item-extralife":   return 2;
175                 case "item-speed":       return 2;
176                 case "item-strength":    return 2;
177                 case "item-shield":      return 2;
178                 case "item-fuelregen":   return 2;
179                 case "item-jetpack":     return 2;
180                 case "tagged-target":    return 2;
181                 default:                 return 1;
182         }
183 }
184 vector spritelookupcolor(string s, vector def)
185 {
186         if(substring(s, 0, 4) == "wpn-")
187                 return (get_weaponinfo(stof(substring(s, 4, strlen(s)))).wpcolor);
188
189         switch(s)
190         {
191                 case "keycarrier-friend": return '0 1 0';
192                 default:                  return def;
193         }
194 }
195 string spritelookuptext(string s)
196 {
197         if(substring(s, 0, 4) == "wpn-") { return (get_weaponinfo(stof(substring(s, 4, strlen(s)))).message); }
198         if(substring(s, 0, 5) == "buff-") { return Buff_PrettyName(Buff_Type_FromSprite(s)); }
199
200         switch(s)
201         {
202                 case "as-push": return _("Push");
203                 case "as-destroy": return _("Destroy");
204                 case "as-defend": return _("Defend");
205                 case "bluebase": return _("Blue base");
206                 case "danger": return _("DANGER");
207                 case "enemyflagcarrier": return _("Enemy carrier");
208                 case "flagcarrier": return _("Flag carrier");
209                 case "flagdropped": return _("Dropped flag");
210                 case "helpme": return _("Help me!");
211                 case "here": return _("Here");
212                 case "key-dropped": return _("Dropped key");
213                 case "keycarrier-blue": return _("Key carrier");
214                 case "keycarrier-finish": return _("Run here");
215                 case "keycarrier-friend": return _("Key carrier");
216                 case "keycarrier-pink": return _("Key carrier");
217                 case "keycarrier-red": return _("Key carrier");
218                 case "keycarrier-yellow": return _("Key carrier");
219                 case "redbase": return _("Red base");
220                 case "waypoint": return _("Waypoint");
221                 case "ons-gen": return _("Generator");
222                 case "ons-gen-shielded": return _("Generator");
223                 case "ons-cp": return _("Control point");
224                 case "ons-cp-atck": return _("Control point");
225                 case "ons-cp-dfnd": return _("Control point");
226                 case "race-checkpoint": return _("Checkpoint");
227                 case "race-finish": return _("Finish");
228                 case "race-start": return _("Start");
229                 case "race-start-finish": return (race_checkpointtime || race_mycheckpointtime) ? _("Finish") : _("Start");
230                 case "goal": return _("Goal");
231                 case "nb-ball": return _("Ball");
232                 case "ka-ball": return _("Ball");
233                 case "ka-ballcarrier": return _("Ball carrier");
234                 case "dom-neut": return _("Control point");
235                 case "dom-red": return _("Control point");
236                 case "dom-blue": return _("Control point");
237                 case "dom-yellow": return _("Control point");
238                 case "dom-pink": return _("Control point");
239                 case "item-invis": return _("Invisibility");
240                 case "item-extralife": return _("Extra life");
241                 case "item-speed": return _("Speed");
242                 case "item-strength": return _("Strength");
243                 case "item-shield": return _("Shield");
244                 case "item-fuelregen": return _("Fuel regen");
245                 case "item-jetpack": return _("Jet Pack");
246                 case "frozen": return _("Frozen!");
247                 case "tagged-target": return _("Tagged");
248                 case "vehicle": return _("Vehicle");
249                 default: return s;
250         }
251 }
252
253 vector fixrgbexcess_move(vector rgb, vector src, vector dst)
254 {
255         vector yvec = '0.299 0.587 0.114';
256         return rgb + dst * ((src * yvec) / (dst * yvec)) * ((rgb - '1 1 1') * src);
257 }
258 vector fixrgbexcess(vector rgb)
259 {
260         if(rgb.x > 1)
261         {
262                 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 1 1');
263                 if(rgb.y > 1)
264                 {
265                         rgb = fixrgbexcess_move(rgb, '0 1 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', '0 1 0');
272                         if(rgb.y > 1)
273                                 rgb.y = 1;
274                 }
275         }
276         else if(rgb.y > 1)
277         {
278                 rgb = fixrgbexcess_move(rgb, '0 1 0', '1 0 1');
279                 if(rgb.x > 1)
280                 {
281                         rgb = fixrgbexcess_move(rgb, '1 0 0', '0 0 1');
282                         if(rgb.z > 1)
283                                 rgb.z = 1;
284                 }
285                 else if(rgb.z > 1)
286                 {
287                         rgb = fixrgbexcess_move(rgb, '0 0 1', '1 0 0');
288                         if(rgb.x > 1)
289                                 rgb.x = 1;
290                 }
291         }
292         else if(rgb.z > 1)
293         {
294                 rgb = fixrgbexcess_move(rgb, '0 0 1', '1 1 0');
295                 if(rgb.x > 1)
296                 {
297                         rgb = fixrgbexcess_move(rgb, '1 0 0', '0 1 0');
298                         if(rgb.y > 1)
299                                 rgb.y = 1;
300                 }
301                 else if(rgb.y > 1)
302                 {
303                         rgb = fixrgbexcess_move(rgb, '0 1 0', '1 0 0');
304                         if(rgb.x > 1)
305                                 rgb.x = 1;
306                 }
307         }
308         return rgb;
309 }
310
311 void Draw_WaypointSprite()
312 {
313         string spriteimage;
314         float t;
315
316         if(self.lifetime)
317                 self.alpha = pow(bound(0, (self.fadetime - time) / self.lifetime, 1), waypointsprite_timealphaexponent);
318         else
319                 self.alpha = 1;
320
321         if(self.hideflags & 2)
322                 return; // radar only
323
324         if(autocvar_cl_hidewaypoints >= 2)
325                 return;
326
327         if(self.hideflags & 1)
328                 if(autocvar_cl_hidewaypoints)
329                         return; // fixed waypoint
330
331         InterpolateOrigin_Do();
332
333         t = GetPlayerColor(player_localnum) + 1;
334
335         spriteimage = "";
336
337         // choose the sprite
338         switch(self.rule)
339         {
340                 case SPRITERULE_DEFAULT:
341                         if(self.team)
342                         {
343                                 if(self.team == t)
344                                         spriteimage = self.netname;
345                                 else
346                                         spriteimage = "";
347                         }
348                         else
349                                 spriteimage = self.netname;
350                         break;
351                 case SPRITERULE_TEAMPLAY:
352                         if(t == NUM_SPECTATOR + 1)
353                                 spriteimage = self.netname3;
354                         else if(self.team == t)
355                                 spriteimage = self.netname2;
356                         else
357                                 spriteimage = self.netname;
358                         break;
359                 default:
360                         error("Invalid waypointsprite rule!");
361                         break;
362         }
363
364         if(spriteimage == "")
365                 return;
366
367         ++waypointsprite_newcount;
368
369         float dist;
370         dist = vlen(self.origin - view_origin);
371
372         float a;
373         a = self.alpha * autocvar_hud_panel_fg_alpha;
374
375         if(self.maxdistance > waypointsprite_normdistance)
376                 a *= pow(bound(0, (self.maxdistance - dist) / (self.maxdistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent);
377         else if(self.maxdistance > 0)
378                 a *= pow(bound(0, (waypointsprite_fadedistance - dist) / (waypointsprite_fadedistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent) * (1 - waypointsprite_minalpha) + waypointsprite_minalpha;
379
380         vector rgb;
381         rgb = self.teamradar_color;
382         rgb = spritelookupcolor(spriteimage, rgb);
383         if(rgb == '0 0 0')
384         {
385                 self.teamradar_color = '1 0 1';
386                 printf("WARNING: sprite of name %s has no color, using pink so you notice it\n", spriteimage);
387         }
388
389         if(time - floor(time) > 0.5)
390         {
391                 if(self.helpme && time < self.helpme)
392                         a *= SPRITE_HELPME_BLINK;
393                 else
394                         a *= spritelookupblinkvalue(spriteimage);
395         }
396
397         if(a > 1)
398         {
399                 rgb *= a;
400                 a = 1;
401         }
402
403         if(a <= 0)
404                 return;
405
406         rgb = fixrgbexcess(rgb);
407
408         vector o;
409         float ang;
410
411         o = project_3d_to_2d(self.origin);
412         if(o.z < 0
413         || o.x < (vid_conwidth * waypointsprite_edgeoffset_left)
414         || o.y < (vid_conheight * waypointsprite_edgeoffset_top)
415         || o.x > (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right))
416         || o.y > (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom)))
417         {
418                 // scale it to be just in view
419                 vector d;
420                 float f1, f2;
421
422                 d = o - '0.5 0 0' * vid_conwidth - '0 0.5 0' * vid_conheight;
423                 ang = atan2(-d.x, -d.y);
424                 if(o.z < 0)
425                         ang += M_PI;
426
427                 f1 = d.x / vid_conwidth;
428                 f2 = d.y / vid_conheight;
429
430                 if(max(f1, -f1) > max(f2, -f2))
431                 {
432                         if(d.z * f1 > 0)
433                         {
434                                 // RIGHT edge
435                                 d = d * ((0.5 - waypointsprite_edgeoffset_right) / f1);
436                         }
437                         else
438                         {
439                                 // LEFT edge
440                                 d = d * (-(0.5 - waypointsprite_edgeoffset_left) / f1);
441                         }
442                 }
443                 else
444                 {
445                         if(d.z * f2 > 0)
446                         {
447                                 // BOTTOM edge
448                                 d = d * ((0.5 - waypointsprite_edgeoffset_bottom) / f2);
449                         }
450                         else
451                         {
452                                 // TOP edge
453                                 d = d * (-(0.5 - waypointsprite_edgeoffset_top) / f2);
454                         }
455                 }
456
457                 o = d + '0.5 0 0' * vid_conwidth + '0 0.5 0' * vid_conheight;
458         }
459         else
460         {
461 #if 1
462                 ang = M_PI;
463 #else
464                 vector d;
465                 d = o - '0.5 0 0' * vid_conwidth - '0 0.5 0' * vid_conheight;
466                 ang = atan2(-d.x, -d.y);
467 #endif
468         }
469         o.z = 0;
470
471         float edgedistance_min, crosshairdistance;
472                 edgedistance_min = min((o.y - (vid_conheight * waypointsprite_edgeoffset_top)),
473         (o.x - (vid_conwidth * waypointsprite_edgeoffset_left)),
474         (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right)) - o.x,
475         (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom)) - o.y);
476
477         float vidscale;
478         vidscale = max(vid_conwidth / vid_width, vid_conheight / vid_height);
479
480         crosshairdistance = sqrt( pow(o.x - vid_conwidth/2, 2) + pow(o.y - vid_conheight/2, 2) );
481
482         t = waypointsprite_scale * vidscale;
483         a *= waypointsprite_alpha;
484
485         {
486                 a = a * (1 - (1 - waypointsprite_distancefadealpha) * (bound(0, dist/waypointsprite_distancefadedistance, 1)));
487                 t = t * (1 - (1 - waypointsprite_distancefadescale) * (bound(0, dist/waypointsprite_distancefadedistance, 1)));
488         }
489         if (edgedistance_min < waypointsprite_edgefadedistance) {
490                 a = a * (1 - (1 - waypointsprite_edgefadealpha) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1)));
491                 t = t * (1 - (1 - waypointsprite_edgefadescale) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1)));
492         }
493         if(crosshairdistance < waypointsprite_crosshairfadedistance) {
494                 a = a * (1 - (1 - waypointsprite_crosshairfadealpha) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1)));
495                 t = t * (1 - (1 - waypointsprite_crosshairfadescale) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1)));
496         }
497
498         if(self.build_finished)
499         {
500                 if(time < self.build_finished + 0.25)
501                 {
502                         if(time < self.build_started)
503                                 self.health = self.build_starthealth;
504                         else if(time < self.build_finished)
505                                 self.health = (time - self.build_started) / (self.build_finished - self.build_started) * (1 - self.build_starthealth) + self.build_starthealth;
506                         else
507                                 self.health = 1;
508                 }
509                 else
510                         self.health = -1;
511         }
512
513         o = drawspritearrow(o, ang, rgb, a, SPRITE_ARROW_SCALE * t);
514
515         string txt;
516         if(autocvar_g_waypointsprite_spam && waypointsprite_count >= autocvar_g_waypointsprite_spam)
517                 txt = _("Spam");
518         else
519                 txt = spritelookuptext(spriteimage);
520         if(self.helpme && time < self.helpme)
521                 txt = sprintf(_("%s needing help!"), txt);
522         if(autocvar_g_waypointsprite_uppercase)
523                 txt = strtoupper(txt);
524
525         draw_beginBoldFont();
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         draw_endBoldFont();
562 }
563
564 void Ent_RemoveWaypointSprite()
565 {
566         if(self.netname)
567                 strunzone(self.netname);
568         if(self.netname2)
569                 strunzone(self.netname2);
570         if(self.netname3)
571                 strunzone(self.netname3);
572 }
573
574 void Ent_WaypointSprite()
575 {
576         int sendflags, f, t;
577         sendflags = ReadByte();
578
579         if(!self.spawntime)
580                 self.spawntime = time;
581
582         self.draw2d = Draw_WaypointSprite;
583
584         InterpolateOrigin_Undo();
585         self.iflags |= IFLAG_ORIGIN;
586
587         if(sendflags & 0x80)
588         {
589                 t = ReadByte();
590                 if(t < 192)
591                 {
592                         self.health = t / 191.0;
593                         self.build_finished = 0;
594                 }
595                 else
596                 {
597                         t = (t - 192) * 256 + ReadByte();
598                         self.build_started = servertime;
599                         if(self.build_finished)
600                                 self.build_starthealth = bound(0, self.health, 1);
601                         else
602                                 self.build_starthealth = 0;
603                         self.build_finished = servertime + t / 32;
604                 }
605         }
606         else
607         {
608                 self.health = -1;
609                 self.build_finished = 0;
610         }
611
612         if(sendflags & 64)
613         {
614                 // unfortunately, this needs to be exact (for the 3D display)
615                 self.origin_x = ReadCoord();
616                 self.origin_y = ReadCoord();
617                 self.origin_z = ReadCoord();
618                 setorigin(self, self.origin);
619         }
620
621         if(sendflags & 1)
622         {
623                 self.team = ReadByte();
624                 self.rule = ReadByte();
625         }
626
627         if(sendflags & 2)
628         {
629                 if(self.netname)
630                         strunzone(self.netname);
631                 self.netname = strzone(ReadString());
632         }
633
634         if(sendflags & 4)
635         {
636                 if(self.netname2)
637                         strunzone(self.netname2);
638                 self.netname2 = strzone(ReadString());
639         }
640
641         if(sendflags & 8)
642         {
643                 if(self.netname3)
644                         strunzone(self.netname3);
645                 self.netname3 = strzone(ReadString());
646         }
647
648         if(sendflags & 16)
649         {
650                 self.lifetime = ReadCoord();
651                 self.fadetime = ReadCoord();
652                 self.maxdistance = ReadShort();
653                 self.hideflags = ReadByte();
654         }
655
656         if(sendflags & 32)
657         {
658                 f = ReadByte();
659                 self.teamradar_icon = (f & 0x7F);
660                 if(f & 0x80)
661                 {
662                         self.(teamradar_times[self.teamradar_time_index]) = time;
663                         self.teamradar_time_index = (self.teamradar_time_index + 1) % MAX_TEAMRADAR_TIMES;
664                 }
665                 self.teamradar_color_x = ReadByte() / 255.0;
666                 self.teamradar_color_y = ReadByte() / 255.0;
667                 self.teamradar_color_z = ReadByte() / 255.0;
668                 self.helpme = ReadByte() * 0.1;
669                 if(self.helpme > 0)
670                         self.helpme += servertime;
671         }
672
673         InterpolateOrigin_Note();
674
675         self.entremove = Ent_RemoveWaypointSprite;
676 }
677
678 void WaypointSprite_Load_Frames(string ext)
679 {
680         float dh, n, i, o, f;
681         string s, sname, sframes;
682         dh = search_begin(strcat("models/sprites/*_frame*", ext), false, false);
683         if (dh < 0)
684                  return;
685         float ext_len = strlen(ext);
686         n = search_getsize(dh);
687         for(i = 0; i < n; ++i)
688         {
689                 s = search_getfilename(dh, i);
690                 s = substring(s, 15, strlen(s) - 15 - ext_len); // strip models/sprites/ and extension
691
692                 o = strstrofs(s, "_frame", 0);
693                 sname = strcat("/spriteframes/", substring(s, 0, o));
694                 sframes = substring(s, o + 6, strlen(s) - o - 6);
695                 f = stof(sframes) + 1;
696                 db_put(tempdb, sname, ftos(max(f, stof(db_get(tempdb, sname)))));
697         }
698         search_end(dh);
699 }
700
701 void WaypointSprite_Load()
702 {
703         waypointsprite_fadedistance = vlen(mi_scale);
704         waypointsprite_normdistance = autocvar_g_waypointsprite_normdistance;
705         waypointsprite_minscale = autocvar_g_waypointsprite_minscale;
706         waypointsprite_minalpha = autocvar_g_waypointsprite_minalpha;
707         waypointsprite_distancealphaexponent = autocvar_g_waypointsprite_distancealphaexponent;
708         waypointsprite_timealphaexponent = autocvar_g_waypointsprite_timealphaexponent;
709         waypointsprite_scale = autocvar_g_waypointsprite_scale;
710         waypointsprite_fontsize = autocvar_g_waypointsprite_fontsize;
711         waypointsprite_edgefadealpha = autocvar_g_waypointsprite_edgefadealpha;
712         waypointsprite_edgefadescale = autocvar_g_waypointsprite_edgefadescale;
713         waypointsprite_edgefadedistance = autocvar_g_waypointsprite_edgefadedistance;
714         waypointsprite_edgeoffset_bottom = autocvar_g_waypointsprite_edgeoffset_bottom;
715         waypointsprite_edgeoffset_left = autocvar_g_waypointsprite_edgeoffset_left;
716         waypointsprite_edgeoffset_right = autocvar_g_waypointsprite_edgeoffset_right;
717         waypointsprite_edgeoffset_top = autocvar_g_waypointsprite_edgeoffset_top;
718         waypointsprite_crosshairfadealpha = autocvar_g_waypointsprite_crosshairfadealpha;
719         waypointsprite_crosshairfadescale = autocvar_g_waypointsprite_crosshairfadescale;
720         waypointsprite_crosshairfadedistance = autocvar_g_waypointsprite_crosshairfadedistance;
721         waypointsprite_distancefadealpha = autocvar_g_waypointsprite_distancefadealpha;
722         waypointsprite_distancefadescale = autocvar_g_waypointsprite_distancefadescale;
723         waypointsprite_distancefadedistance = waypointsprite_fadedistance * autocvar_g_waypointsprite_distancefadedistancemultiplier;
724         waypointsprite_alpha = autocvar_g_waypointsprite_alpha * (1 - autocvar__menu_alpha);
725
726         if(!waypointsprite_initialized)
727         {
728                 WaypointSprite_Load_Frames(".tga");
729                 WaypointSprite_Load_Frames(".jpg");
730                 waypointsprite_initialized = true;
731         }
732
733         waypointsprite_count = waypointsprite_newcount;
734         waypointsprite_newcount = 0;
735 }