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