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 "waypoint": return _("Waypoint");
255 case "ons-gen-red": return _("Generator");
256 case "ons-gen-blue": return _("Generator");
257 case "ons-gen-shielded": return _("Generator");
258 case "ons-cp-neut": return _("Control point");
259 case "ons-cp-red": return _("Control point");
260 case "ons-cp-blue": return _("Control point");
261 case "ons-cp-atck-neut": return _("Control point");
262 case "ons-cp-atck-red": return _("Control point");
263 case "ons-cp-atck-blue": return _("Control point");
264 case "ons-cp-dfnd-red": return _("Control point");
265 case "ons-cp-dfnd-blue": 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 "dom-neut": return _("Control point");
275 case "dom-red": return _("Control point");
276 case "dom-blue": return _("Control point");
277 case "dom-yellow": return _("Control point");
278 case "dom-pink": return _("Control point");
279 case "item-invis": return _("Invisibility");
280 case "item-extralife": return _("Extra life");
281 case "item-speed": return _("Speed");
282 case "item-strength": return _("Strength");
283 case "item-shield": return _("Shield");
284 case "item-fuelregen": return _("Fuel regen");
285 case "item-jetpack": return _("Jet Pack");
286 case "frozen": return _("Frozen!");
287 case "tagged-target": return _("Tagged");
288 case "vehicle": return _("Vehicle");
293 vector fixrgbexcess_move(vector rgb, vector src, vector dst)
295 vector yvec = '0.299 0.587 0.114';
296 return rgb + dst * ((src * yvec) / (dst * yvec)) * ((rgb - '1 1 1') * src);
298 vector fixrgbexcess(vector rgb)
302 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 1 1');
305 rgb = fixrgbexcess_move(rgb, '0 1 0', '0 0 1');
311 rgb = fixrgbexcess_move(rgb, '0 0 1', '0 1 0');
318 rgb = fixrgbexcess_move(rgb, '0 1 0', '1 0 1');
321 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 0 1');
327 rgb = fixrgbexcess_move(rgb, '0 0 1', '1 0 0');
334 rgb = fixrgbexcess_move(rgb, '0 0 1', '1 1 0');
337 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 1 0');
343 rgb = fixrgbexcess_move(rgb, '0 1 0', '1 0 0');
351 float waypointsprite_count, waypointsprite_newcount;
352 void Draw_WaypointSprite()
358 self.alpha = pow(bound(0, (self.fadetime - time) / self.lifetime, 1), waypointsprite_timealphaexponent);
362 if(self.hideflags & 2)
363 return; // radar only
365 if(autocvar_cl_hidewaypoints >= 2)
368 if(self.hideflags & 1)
369 if(autocvar_cl_hidewaypoints)
370 return; // fixed waypoint
372 InterpolateOrigin_Do();
374 t = GetPlayerColor(player_localnum) + 1;
381 case SPRITERULE_DEFAULT:
385 spriteimage = self.netname;
390 spriteimage = self.netname;
392 case SPRITERULE_TEAMPLAY:
393 if(t == NUM_SPECTATOR + 1)
394 spriteimage = self.netname3;
395 else if(self.team == t)
396 spriteimage = self.netname2;
398 spriteimage = self.netname;
401 error("Invalid waypointsprite rule!");
405 if(spriteimage == "")
408 ++waypointsprite_newcount;
411 dist = vlen(self.origin - view_origin);
414 a = self.alpha * autocvar_hud_panel_fg_alpha;
416 if(self.maxdistance > waypointsprite_normdistance)
417 a *= pow(bound(0, (self.maxdistance - dist) / (self.maxdistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent);
418 else if(self.maxdistance > 0)
419 a *= pow(bound(0, (waypointsprite_fadedistance - dist) / (waypointsprite_fadedistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent) * (1 - waypointsprite_minalpha) + waypointsprite_minalpha;
422 rgb = self.teamradar_color;
423 rgb = spritelookupcolor(spriteimage, rgb);
426 self.teamradar_color = '1 0 1';
427 printf("WARNING: sprite of name %s has no color, using pink so you notice it\n", spriteimage);
430 if(time - floor(time) > 0.5)
432 if(self.helpme && time < self.helpme)
433 a *= SPRITE_HELPME_BLINK;
435 a *= spritelookupblinkvalue(spriteimage);
447 rgb = fixrgbexcess(rgb);
452 o = project_3d_to_2d(self.origin);
454 || o_x < (vid_conwidth * waypointsprite_edgeoffset_left)
455 || o_y < (vid_conheight * waypointsprite_edgeoffset_top)
456 || o_x > (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right))
457 || o_y > (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom)))
459 // scale it to be just in view
463 d = o - '0.5 0 0' * vid_conwidth - '0 0.5 0' * vid_conheight;
464 ang = atan2(-d_x, -d_y);
468 f1 = d_x / vid_conwidth;
469 f2 = d_y / vid_conheight;
471 if(max(f1, -f1) > max(f2, -f2))
476 d = d * ((0.5 - waypointsprite_edgeoffset_right) / f1);
481 d = d * (-(0.5 - waypointsprite_edgeoffset_left) / f1);
489 d = d * ((0.5 - waypointsprite_edgeoffset_bottom) / f2);
494 d = d * (-(0.5 - waypointsprite_edgeoffset_top) / f2);
498 o = d + '0.5 0 0' * vid_conwidth + '0 0.5 0' * vid_conheight;
506 d = o - '0.5 0 0' * vid_conwidth - '0 0.5 0' * vid_conheight;
507 ang = atan2(-d_x, -d_y);
512 float edgedistance_min, crosshairdistance;
513 edgedistance_min = min((o_y - (vid_conheight * waypointsprite_edgeoffset_top)),
514 (o_x - (vid_conwidth * waypointsprite_edgeoffset_left)),
515 (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right)) - o_x,
516 (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom)) - o_y);
519 vidscale = max(vid_conwidth / vid_width, vid_conheight / vid_height);
521 crosshairdistance = sqrt( pow(o_x - vid_conwidth/2, 2) + pow(o_y - vid_conheight/2, 2) );
523 t = waypointsprite_scale * vidscale;
524 a *= waypointsprite_alpha;
527 a = a * (1 - (1 - waypointsprite_distancefadealpha) * (bound(0, dist/waypointsprite_distancefadedistance, 1)));
528 t = t * (1 - (1 - waypointsprite_distancefadescale) * (bound(0, dist/waypointsprite_distancefadedistance, 1)));
530 if (edgedistance_min < waypointsprite_edgefadedistance) {
531 a = a * (1 - (1 - waypointsprite_edgefadealpha) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1)));
532 t = t * (1 - (1 - waypointsprite_edgefadescale) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1)));
534 if(crosshairdistance < waypointsprite_crosshairfadedistance) {
535 a = a * (1 - (1 - waypointsprite_crosshairfadealpha) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1)));
536 t = t * (1 - (1 - waypointsprite_crosshairfadescale) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1)));
539 if(self.build_finished)
541 if(time < self.build_finished + 0.25)
543 if(time < self.build_started)
544 self.health = self.build_starthealth;
545 else if(time < self.build_finished)
546 self.health = (time - self.build_started) / (self.build_finished - self.build_started) * (1 - self.build_starthealth) + self.build_starthealth;
554 o = drawspritearrow(o, ang, rgb, a, SPRITE_ARROW_SCALE * t);
557 if(autocvar_g_waypointsprite_spam && waypointsprite_count >= autocvar_g_waypointsprite_spam)
560 txt = spritelookuptext(spriteimage);
561 if(self.helpme && time < self.helpme)
562 txt = sprintf(_("%s needing help!"), txt);
563 if(autocvar_g_waypointsprite_uppercase)
564 txt = strtoupper(txt);
566 draw_beginBoldFont();
569 o = drawspritetext(o, ang, (SPRITE_HEALTHBAR_WIDTH + 2 * SPRITE_HEALTHBAR_BORDER) * t, rgb, a, waypointsprite_fontsize * '1 1 0', txt);
572 if(self.build_finished)
577 marg = -(SPRITE_HEALTHBAR_MARGIN + SPRITE_HEALTHBAR_HEIGHT + 2 * SPRITE_HEALTHBAR_BORDER) * t - 0.5 * waypointsprite_fontsize;
579 marg = SPRITE_HEALTHBAR_MARGIN * t + 0.5 * waypointsprite_fontsize;
586 SPRITE_HEALTHBAR_WIDTH * t,
587 SPRITE_HEALTHBAR_HEIGHT * t,
589 SPRITE_HEALTHBAR_BORDER * t,
592 a * SPRITE_HEALTHBAR_BORDERALPHA,
594 a * SPRITE_HEALTHBAR_HEALTHALPHA,
600 o = drawspritetext(o, ang, 0, rgb, a, waypointsprite_fontsize * '1 1 0', txt);
605 void Ent_RemoveWaypointSprite()
608 strunzone(self.netname);
610 strunzone(self.netname2);
612 strunzone(self.netname3);
615 void Ent_WaypointSprite()
617 float sendflags, f, t;
618 sendflags = ReadByte();
621 self.spawntime = time;
623 self.draw2d = Draw_WaypointSprite;
625 InterpolateOrigin_Undo();
626 self.iflags |= IFLAG_ORIGIN;
633 self.health = t / 191.0;
634 self.build_finished = 0;
638 t = (t - 192) * 256 + ReadByte();
639 self.build_started = servertime;
640 if(self.build_finished)
641 self.build_starthealth = bound(0, self.health, 1);
643 self.build_starthealth = 0;
644 self.build_finished = servertime + t / 32;
650 self.build_finished = 0;
655 // unfortunately, this needs to be exact (for the 3D display)
656 self.origin_x = ReadCoord();
657 self.origin_y = ReadCoord();
658 self.origin_z = ReadCoord();
659 setorigin(self, self.origin);
664 self.team = ReadByte();
665 self.rule = ReadByte();
671 strunzone(self.netname);
672 self.netname = strzone(ReadString());
678 strunzone(self.netname2);
679 self.netname2 = strzone(ReadString());
685 strunzone(self.netname3);
686 self.netname3 = strzone(ReadString());
691 self.lifetime = ReadCoord();
692 self.fadetime = ReadCoord();
693 self.maxdistance = ReadShort();
694 self.hideflags = ReadByte();
700 self.teamradar_icon = (f & 0x7F);
703 self.(teamradar_times[self.teamradar_time_index]) = time;
704 self.teamradar_time_index = (self.teamradar_time_index + 1) % MAX_TEAMRADAR_TIMES;
706 self.teamradar_color_x = ReadByte() / 255.0;
707 self.teamradar_color_y = ReadByte() / 255.0;
708 self.teamradar_color_z = ReadByte() / 255.0;
709 self.helpme = ReadByte() * 0.1;
711 self.helpme += servertime;
714 InterpolateOrigin_Note();
716 self.entremove = Ent_RemoveWaypointSprite;
719 void WaypointSprite_Load_Frames(string ext)
721 float dh, n, i, o, f;
722 string s, sname, sframes;
723 dh = search_begin(strcat("models/sprites/*_frame*", ext), FALSE, FALSE);
726 float ext_len = strlen(ext);
727 n = search_getsize(dh);
728 for(i = 0; i < n; ++i)
730 s = search_getfilename(dh, i);
731 s = substring(s, 15, strlen(s) - 15 - ext_len); // strip models/sprites/ and extension
733 o = strstrofs(s, "_frame", 0);
734 sname = strcat("/spriteframes/", substring(s, 0, o));
735 sframes = substring(s, o + 6, strlen(s) - o - 6);
736 f = stof(sframes) + 1;
737 db_put(tempdb, sname, ftos(max(f, stof(db_get(tempdb, sname)))));
742 void WaypointSprite_Load()
744 waypointsprite_fadedistance = vlen(mi_scale);
745 waypointsprite_normdistance = autocvar_g_waypointsprite_normdistance;
746 waypointsprite_minscale = autocvar_g_waypointsprite_minscale;
747 waypointsprite_minalpha = autocvar_g_waypointsprite_minalpha;
748 waypointsprite_distancealphaexponent = autocvar_g_waypointsprite_distancealphaexponent;
749 waypointsprite_timealphaexponent = autocvar_g_waypointsprite_timealphaexponent;
750 waypointsprite_scale = autocvar_g_waypointsprite_scale;
751 waypointsprite_fontsize = autocvar_g_waypointsprite_fontsize;
752 waypointsprite_edgefadealpha = autocvar_g_waypointsprite_edgefadealpha;
753 waypointsprite_edgefadescale = autocvar_g_waypointsprite_edgefadescale;
754 waypointsprite_edgefadedistance = autocvar_g_waypointsprite_edgefadedistance;
755 waypointsprite_edgeoffset_bottom = autocvar_g_waypointsprite_edgeoffset_bottom;
756 waypointsprite_edgeoffset_left = autocvar_g_waypointsprite_edgeoffset_left;
757 waypointsprite_edgeoffset_right = autocvar_g_waypointsprite_edgeoffset_right;
758 waypointsprite_edgeoffset_top = autocvar_g_waypointsprite_edgeoffset_top;
759 waypointsprite_crosshairfadealpha = autocvar_g_waypointsprite_crosshairfadealpha;
760 waypointsprite_crosshairfadescale = autocvar_g_waypointsprite_crosshairfadescale;
761 waypointsprite_crosshairfadedistance = autocvar_g_waypointsprite_crosshairfadedistance;
762 waypointsprite_distancefadealpha = autocvar_g_waypointsprite_distancefadealpha;
763 waypointsprite_distancefadescale = autocvar_g_waypointsprite_distancefadescale;
764 waypointsprite_distancefadedistance = waypointsprite_fadedistance * autocvar_g_waypointsprite_distancefadedistancemultiplier;
765 waypointsprite_alpha = autocvar_g_waypointsprite_alpha * (1 - autocvar__menu_alpha);
767 if(!waypointsprite_initialized)
769 WaypointSprite_Load_Frames(".tga");
770 WaypointSprite_Load_Frames(".jpg");
771 waypointsprite_initialized = true;
774 waypointsprite_count = waypointsprite_newcount;
775 waypointsprite_newcount = 0;