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