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