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;
27 .string netname; // primary picture
28 .string netname2; // secondary picture
29 .string netname3; // tertiary picture
30 .float team; // team that gets netname2
38 .float build_starthealth;
39 .float build_finished;
41 const float SPRITE_HEALTHBAR_WIDTH = 144;
42 const float SPRITE_HEALTHBAR_HEIGHT = 9;
43 const float SPRITE_HEALTHBAR_MARGIN = 6;
44 const float SPRITE_HEALTHBAR_BORDER = 2;
45 const float SPRITE_HEALTHBAR_BORDERALPHA = 1;
46 const float SPRITE_HEALTHBAR_HEALTHALPHA = 0.5;
47 const float SPRITE_ARROW_SCALE = 1.0;
48 const float SPRITE_HELPME_BLINK = 2;
50 void drawrotpic(vector org, float rot, string pic, vector sz, vector hotspot, vector rgb, float a, float f)
52 vector v1, v2, v3, v4;
54 hotspot = -1 * hotspot;
56 // hotspot-relative coordinates of the corners
58 v2 = hotspot + '1 0 0' * sz_x;
59 v3 = hotspot + '1 0 0' * sz_x + '0 1 0' * sz_y;
60 v4 = hotspot + '0 1 0' * sz_y;
62 // rotate them, and make them absolute
63 rot = -rot; // rotate by the opposite angle, as our coordinate system is reversed
64 v1 = rotate(v1, rot) + org;
65 v2 = rotate(v2, rot) + org;
66 v3 = rotate(v3, rot) + org;
67 v4 = rotate(v4, rot) + org;
70 R_BeginPolygon(pic, f);
71 R_PolygonVertex(v1, '0 0 0', rgb, a);
72 R_PolygonVertex(v2, '1 0 0', rgb, a);
73 R_PolygonVertex(v3, '1 1 0', rgb, a);
74 R_PolygonVertex(v4, '0 1 0', rgb, a);
78 void drawquad(vector o, vector ri, vector up, string pic, vector rgb, float a, float f)
80 R_BeginPolygon(pic, f);
81 R_PolygonVertex(o, '0 0 0', rgb, a);
82 R_PolygonVertex(o + ri, '1 0 0', rgb, a);
83 R_PolygonVertex(o + up + ri, '1 1 0', rgb, a);
84 R_PolygonVertex(o + up, '0 1 0', rgb, a);
88 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)
91 float owidth; // outer width
93 hotspot = -1 * hotspot;
95 // hotspot-relative coordinates of the healthbar corners
100 rot = -rot; // rotate by the opposite angle, as our coordinate system is reversed
101 o = rotate(o, rot) + org;
102 ri = rotate(ri, rot);
103 up = rotate(up, rot);
105 owidth = width + 2 * border;
106 o = o - up * (margin + border + height) + ri * (sz_x - owidth) * 0.5;
108 drawquad(o - up * border, ri * owidth, up * border, "", rgb, a, f);
109 drawquad(o + up * height, ri * owidth, up * border, "", rgb, a, f);
110 drawquad(o, ri * border, up * height, "", rgb, a, f);
111 drawquad(o + ri * (owidth - border), ri * border, up * height, "", rgb, a, f);
112 drawquad(o + ri * (border + align * ((1 - h) * width)), ri * width * h, up * height, "", hrgb, ha, f);
115 // returns location of sprite text
116 vector drawspritearrow(vector o, float ang, vector rgb, float a, float t)
118 float size = 9.0 * t;
119 float border = 1.5 * t;
120 float margin = 4.0 * t;
122 float borderDiag = border * 1.414;
123 vector arrowX = eX * size;
124 vector arrowY = eY * (size+borderDiag);
125 vector borderX = eX * (size+borderDiag);
126 vector borderY = eY * (size+borderDiag+border);
128 R_BeginPolygon("", DRAWFLAG_NORMAL);
129 R_PolygonVertex(o, '0 0 0', '0 0 0', a);
130 R_PolygonVertex(o + rotate(arrowY - 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(borderY + borderX, ang), '0 0 0', '0 0 0', a);
133 R_PolygonVertex(o + rotate(arrowY + borderX, ang), '0 0 0', '0 0 0', a);
136 R_BeginPolygon("", DRAWFLAG_ADDITIVE);
137 R_PolygonVertex(o + rotate(eY * borderDiag, ang), '0 0 0', rgb, a);
138 R_PolygonVertex(o + rotate(arrowY - arrowX, ang), '0 0 0', rgb, a);
139 R_PolygonVertex(o + rotate(arrowY + arrowX, ang), '0 0 0', rgb, a);
142 return o + rotate(eY * (borderDiag+size+margin), ang);
145 // returns location of sprite healthbar
146 vector drawspritetext(vector o, float ang, float minwidth, vector rgb, float a, vector fontsize, string s)
150 float aspect, sa, ca;
152 sw = stringwidth(s, FALSE, fontsize);
159 // how do corners work?
160 aspect = vid_conwidth / vid_conheight;
162 ca = cos(ang) * aspect;
163 if(fabs(sa) > fabs(ca))
166 algny = 0.5 - 0.5 * ca / fabs(sa);
170 algnx = 0.5 - 0.5 * sa / fabs(ca);
178 // we want to be onscreen
183 if(o_x > vid_conwidth - w)
184 o_x = vid_conwidth - w;
185 if(o_y > vid_conheight - h)
186 o_x = vid_conheight - h;
188 o_x += 0.5 * (w - sw);
190 drawstring(o, s, fontsize, rgb, a, DRAWFLAG_NORMAL);
198 float spritelookupblinkvalue(string s)
202 case "ons-cp-atck-neut": return 2;
203 case "ons-cp-atck-red": return 2;
204 case "ons-cp-atck-blue": return 2;
205 case "ons-cp-dfnd-red": return 0.5;
206 case "ons-cp-dfnd-blue": return 0.5;
207 case "item-invis": return 2;
208 case "item-extralife": return 2;
209 case "item-speed": return 2;
210 case "item-strength": return 2;
211 case "item-shield": return 2;
212 case "item-fuelregen": return 2;
213 case "item-jetpack": return 2;
214 case "tagged-target": return 2;
218 vector spritelookupcolor(string s, vector def)
220 if(substring(s, 0, 4) == "wpn-")
221 return (get_weaponinfo(stof(substring(s, 4, strlen(s)))).wpcolor);
225 case "keycarrier-friend": return '0 1 0';
229 string spritelookuptext(string s)
231 if(substring(s, 0, 4) == "wpn-") { return (get_weaponinfo(stof(substring(s, 4, strlen(s)))).message); }
232 if(substring(s, 0, 5) == "buff-") { return Buff_PrettyName(Buff_Type_FromSprite(s)); }
236 case "as-push": return _("Push");
237 case "as-destroy": return _("Destroy");
238 case "as-defend": return _("Defend");
239 case "bluebase": return _("Blue base");
240 case "danger": return _("DANGER");
241 case "enemyflagcarrier": return _("Enemy carrier");
242 case "flagcarrier": return _("Flag carrier");
243 case "flagdropped": return _("Dropped flag");
244 case "helpme": return _("Help me!");
245 case "here": return _("Here");
246 case "key-dropped": return _("Dropped key");
247 case "keycarrier-blue": return _("Key carrier");
248 case "keycarrier-finish": return _("Run here");
249 case "keycarrier-friend": return _("Key carrier");
250 case "keycarrier-pink": return _("Key carrier");
251 case "keycarrier-red": return _("Key carrier");
252 case "keycarrier-yellow": return _("Key carrier");
253 case "redbase": return _("Red base");
254 case "yellowbase": return _("Yellow base");
255 case "neutralbase": return _("White base");
256 case "pinkbase": return _("Pink base");
257 case "waypoint": return _("Waypoint");
258 case "ons-gen-red": return _("Generator");
259 case "ons-gen-blue": return _("Generator");
260 case "ons-gen-shielded": return _("Generator");
261 case "ons-cp-neut": return _("Control point");
262 case "ons-cp-red": return _("Control point");
263 case "ons-cp-blue": return _("Control point");
264 case "ons-cp-atck-neut": return _("Control point");
265 case "ons-cp-atck-red": return _("Control point");
266 case "ons-cp-atck-blue": return _("Control point");
267 case "ons-cp-dfnd-red": return _("Control point");
268 case "ons-cp-dfnd-blue": return _("Control point");
269 case "race-checkpoint": return _("Checkpoint");
270 case "race-finish": return _("Finish");
271 case "race-start": return _("Start");
272 case "race-start-finish": return (race_checkpointtime || race_mycheckpointtime) ? _("Finish") : _("Start");
273 case "goal": return _("Goal");
274 case "nb-ball": return _("Ball");
275 case "ka-ball": return _("Ball");
276 case "ka-ballcarrier": return _("Ball carrier");
277 case "dom-neut": return _("Control point");
278 case "dom-red": return _("Control point");
279 case "dom-blue": return _("Control point");
280 case "dom-yellow": return _("Control point");
281 case "dom-pink": return _("Control point");
282 case "item-invis": return _("Invisibility");
283 case "item-extralife": return _("Extra life");
284 case "item-speed": return _("Speed");
285 case "item-strength": return _("Strength");
286 case "item-shield": return _("Shield");
287 case "item-fuelregen": return _("Fuel regen");
288 case "item-jetpack": return _("Jet Pack");
289 case "frozen": return _("Frozen!");
290 case "tagged-target": return _("Tagged");
291 case "vehicle": return _("Vehicle");
296 vector fixrgbexcess_move(vector rgb, vector src, vector dst)
298 vector yvec = '0.299 0.587 0.114';
299 return rgb + dst * ((src * yvec) / (dst * yvec)) * ((rgb - '1 1 1') * src);
301 vector fixrgbexcess(vector rgb)
305 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 1 1');
308 rgb = fixrgbexcess_move(rgb, '0 1 0', '0 0 1');
314 rgb = fixrgbexcess_move(rgb, '0 0 1', '0 1 0');
321 rgb = fixrgbexcess_move(rgb, '0 1 0', '1 0 1');
324 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 0 1');
330 rgb = fixrgbexcess_move(rgb, '0 0 1', '1 0 0');
337 rgb = fixrgbexcess_move(rgb, '0 0 1', '1 1 0');
340 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 1 0');
346 rgb = fixrgbexcess_move(rgb, '0 1 0', '1 0 0');
354 float waypointsprite_count, waypointsprite_newcount;
355 void Draw_WaypointSprite()
361 self.alpha = pow(bound(0, (self.fadetime - time) / self.lifetime, 1), waypointsprite_timealphaexponent);
365 if(self.hideflags & 2)
366 return; // radar only
368 if(autocvar_cl_hidewaypoints >= 2)
371 if(self.hideflags & 1)
372 if(autocvar_cl_hidewaypoints)
373 return; // fixed waypoint
375 InterpolateOrigin_Do();
377 t = GetPlayerColor(player_localnum) + 1;
384 case SPRITERULE_DEFAULT:
388 spriteimage = self.netname;
393 spriteimage = self.netname;
395 case SPRITERULE_TEAMPLAY:
396 if(t == NUM_SPECTATOR + 1)
397 spriteimage = self.netname3;
398 else if(self.team == t)
399 spriteimage = self.netname2;
401 spriteimage = self.netname;
404 error("Invalid waypointsprite rule!");
408 if(spriteimage == "")
411 ++waypointsprite_newcount;
414 dist = vlen(self.origin - view_origin);
417 a = self.alpha * autocvar_hud_panel_fg_alpha;
419 if(self.maxdistance > waypointsprite_normdistance)
420 a *= pow(bound(0, (self.maxdistance - dist) / (self.maxdistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent);
421 else if(self.maxdistance > 0)
422 a *= pow(bound(0, (waypointsprite_fadedistance - dist) / (waypointsprite_fadedistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent) * (1 - waypointsprite_minalpha) + waypointsprite_minalpha;
425 rgb = self.teamradar_color;
426 rgb = spritelookupcolor(spriteimage, rgb);
429 self.teamradar_color = '1 0 1';
430 printf("WARNING: sprite of name %s has no color, using pink so you notice it\n", spriteimage);
433 if(time - floor(time) > 0.5)
435 if(self.helpme && time < self.helpme)
436 a *= SPRITE_HELPME_BLINK;
438 a *= spritelookupblinkvalue(spriteimage);
450 rgb = fixrgbexcess(rgb);
455 o = project_3d_to_2d(self.origin);
457 || o_x < (vid_conwidth * waypointsprite_edgeoffset_left)
458 || o_y < (vid_conheight * waypointsprite_edgeoffset_top)
459 || o_x > (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right))
460 || o_y > (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom)))
462 // scale it to be just in view
466 d = o - '0.5 0 0' * vid_conwidth - '0 0.5 0' * vid_conheight;
467 ang = atan2(-d_x, -d_y);
471 f1 = d_x / vid_conwidth;
472 f2 = d_y / vid_conheight;
474 if(max(f1, -f1) > max(f2, -f2))
479 d = d * ((0.5 - waypointsprite_edgeoffset_right) / f1);
484 d = d * (-(0.5 - waypointsprite_edgeoffset_left) / f1);
492 d = d * ((0.5 - waypointsprite_edgeoffset_bottom) / f2);
497 d = d * (-(0.5 - waypointsprite_edgeoffset_top) / f2);
501 o = d + '0.5 0 0' * vid_conwidth + '0 0.5 0' * vid_conheight;
509 d = o - '0.5 0 0' * vid_conwidth - '0 0.5 0' * vid_conheight;
510 ang = atan2(-d_x, -d_y);
515 float edgedistance_min, crosshairdistance;
516 edgedistance_min = min((o_y - (vid_conheight * waypointsprite_edgeoffset_top)),
517 (o_x - (vid_conwidth * waypointsprite_edgeoffset_left)),
518 (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right)) - o_x,
519 (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom)) - o_y);
522 vidscale = max(vid_conwidth / vid_width, vid_conheight / vid_height);
524 crosshairdistance = sqrt( pow(o_x - vid_conwidth/2, 2) + pow(o_y - vid_conheight/2, 2) );
526 t = waypointsprite_scale * vidscale;
527 a *= waypointsprite_alpha;
530 a = a * (1 - (1 - waypointsprite_distancefadealpha) * (bound(0, dist/waypointsprite_distancefadedistance, 1)));
531 t = t * (1 - (1 - waypointsprite_distancefadescale) * (bound(0, dist/waypointsprite_distancefadedistance, 1)));
533 if (edgedistance_min < waypointsprite_edgefadedistance) {
534 a = a * (1 - (1 - waypointsprite_edgefadealpha) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1)));
535 t = t * (1 - (1 - waypointsprite_edgefadescale) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1)));
537 if(crosshairdistance < waypointsprite_crosshairfadedistance) {
538 a = a * (1 - (1 - waypointsprite_crosshairfadealpha) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1)));
539 t = t * (1 - (1 - waypointsprite_crosshairfadescale) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1)));
542 if(self.build_finished)
544 if(time < self.build_finished + 0.25)
546 if(time < self.build_started)
547 self.health = self.build_starthealth;
548 else if(time < self.build_finished)
549 self.health = (time - self.build_started) / (self.build_finished - self.build_started) * (1 - self.build_starthealth) + self.build_starthealth;
557 o = drawspritearrow(o, ang, rgb, a, SPRITE_ARROW_SCALE * t);
560 if(autocvar_g_waypointsprite_spam && waypointsprite_count >= autocvar_g_waypointsprite_spam)
563 txt = spritelookuptext(spriteimage);
564 if(self.helpme && time < self.helpme)
565 txt = sprintf(_("%s needing help!"), txt);
566 if(autocvar_g_waypointsprite_uppercase)
567 txt = strtoupper(txt);
569 draw_beginBoldFont();
572 o = drawspritetext(o, ang, (SPRITE_HEALTHBAR_WIDTH + 2 * SPRITE_HEALTHBAR_BORDER) * t, rgb, a, waypointsprite_fontsize * '1 1 0', txt);
575 if(self.build_finished)
580 marg = -(SPRITE_HEALTHBAR_MARGIN + SPRITE_HEALTHBAR_HEIGHT + 2 * SPRITE_HEALTHBAR_BORDER) * t - 0.5 * waypointsprite_fontsize;
582 marg = SPRITE_HEALTHBAR_MARGIN * t + 0.5 * waypointsprite_fontsize;
589 SPRITE_HEALTHBAR_WIDTH * t,
590 SPRITE_HEALTHBAR_HEIGHT * t,
592 SPRITE_HEALTHBAR_BORDER * t,
595 a * SPRITE_HEALTHBAR_BORDERALPHA,
597 a * SPRITE_HEALTHBAR_HEALTHALPHA,
603 o = drawspritetext(o, ang, 0, rgb, a, waypointsprite_fontsize * '1 1 0', txt);
608 void Ent_RemoveWaypointSprite()
611 strunzone(self.netname);
613 strunzone(self.netname2);
615 strunzone(self.netname3);
618 void Ent_WaypointSprite()
620 float sendflags, f, t;
621 sendflags = ReadByte();
624 self.spawntime = time;
626 self.draw2d = Draw_WaypointSprite;
628 InterpolateOrigin_Undo();
629 self.iflags |= IFLAG_ORIGIN;
636 self.health = t / 191.0;
637 self.build_finished = 0;
641 t = (t - 192) * 256 + ReadByte();
642 self.build_started = servertime;
643 if(self.build_finished)
644 self.build_starthealth = bound(0, self.health, 1);
646 self.build_starthealth = 0;
647 self.build_finished = servertime + t / 32;
653 self.build_finished = 0;
658 // unfortunately, this needs to be exact (for the 3D display)
659 self.origin_x = ReadCoord();
660 self.origin_y = ReadCoord();
661 self.origin_z = ReadCoord();
662 setorigin(self, self.origin);
667 self.team = ReadByte();
668 self.rule = ReadByte();
674 strunzone(self.netname);
675 self.netname = strzone(ReadString());
681 strunzone(self.netname2);
682 self.netname2 = strzone(ReadString());
688 strunzone(self.netname3);
689 self.netname3 = strzone(ReadString());
694 self.lifetime = ReadCoord();
695 self.fadetime = ReadCoord();
696 self.maxdistance = ReadShort();
697 self.hideflags = ReadByte();
703 self.teamradar_icon = (f & 0x7F);
706 self.(teamradar_times[self.teamradar_time_index]) = time;
707 self.teamradar_time_index = (self.teamradar_time_index + 1) % MAX_TEAMRADAR_TIMES;
709 self.teamradar_color_x = ReadByte() / 255.0;
710 self.teamradar_color_y = ReadByte() / 255.0;
711 self.teamradar_color_z = ReadByte() / 255.0;
712 self.helpme = ReadByte() * 0.1;
714 self.helpme += servertime;
717 InterpolateOrigin_Note();
719 self.entremove = Ent_RemoveWaypointSprite;
722 void WaypointSprite_Load_Frames(string ext)
724 float dh, n, i, o, f;
725 string s, sname, sframes;
726 dh = search_begin(strcat("models/sprites/*_frame*", ext), FALSE, FALSE);
729 float ext_len = strlen(ext);
730 n = search_getsize(dh);
731 for(i = 0; i < n; ++i)
733 s = search_getfilename(dh, i);
734 s = substring(s, 15, strlen(s) - 15 - ext_len); // strip models/sprites/ and extension
736 o = strstrofs(s, "_frame", 0);
737 sname = strcat("/spriteframes/", substring(s, 0, o));
738 sframes = substring(s, o + 6, strlen(s) - o - 6);
739 f = stof(sframes) + 1;
740 db_put(tempdb, sname, ftos(max(f, stof(db_get(tempdb, sname)))));
745 void WaypointSprite_Load()
747 waypointsprite_fadedistance = vlen(mi_scale);
748 waypointsprite_normdistance = autocvar_g_waypointsprite_normdistance;
749 waypointsprite_minscale = autocvar_g_waypointsprite_minscale;
750 waypointsprite_minalpha = autocvar_g_waypointsprite_minalpha;
751 waypointsprite_distancealphaexponent = autocvar_g_waypointsprite_distancealphaexponent;
752 waypointsprite_timealphaexponent = autocvar_g_waypointsprite_timealphaexponent;
753 waypointsprite_scale = autocvar_g_waypointsprite_scale;
754 waypointsprite_fontsize = autocvar_g_waypointsprite_fontsize;
755 waypointsprite_edgefadealpha = autocvar_g_waypointsprite_edgefadealpha;
756 waypointsprite_edgefadescale = autocvar_g_waypointsprite_edgefadescale;
757 waypointsprite_edgefadedistance = autocvar_g_waypointsprite_edgefadedistance;
758 waypointsprite_edgeoffset_bottom = autocvar_g_waypointsprite_edgeoffset_bottom;
759 waypointsprite_edgeoffset_left = autocvar_g_waypointsprite_edgeoffset_left;
760 waypointsprite_edgeoffset_right = autocvar_g_waypointsprite_edgeoffset_right;
761 waypointsprite_edgeoffset_top = autocvar_g_waypointsprite_edgeoffset_top;
762 waypointsprite_crosshairfadealpha = autocvar_g_waypointsprite_crosshairfadealpha;
763 waypointsprite_crosshairfadescale = autocvar_g_waypointsprite_crosshairfadescale;
764 waypointsprite_crosshairfadedistance = autocvar_g_waypointsprite_crosshairfadedistance;
765 waypointsprite_distancefadealpha = autocvar_g_waypointsprite_distancefadealpha;
766 waypointsprite_distancefadescale = autocvar_g_waypointsprite_distancefadescale;
767 waypointsprite_distancefadedistance = waypointsprite_fadedistance * autocvar_g_waypointsprite_distancefadedistancemultiplier;
768 waypointsprite_alpha = autocvar_g_waypointsprite_alpha * (1 - autocvar__menu_alpha);
770 if(!waypointsprite_initialized)
772 WaypointSprite_Load_Frames(".tga");
773 WaypointSprite_Load_Frames(".jpg");
774 waypointsprite_initialized = true;
777 waypointsprite_count = waypointsprite_newcount;
778 waypointsprite_newcount = 0;