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
37 .float build_starthealth;
38 .float build_finished;
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;
49 void drawrotpic(vector org, float rot, string pic, vector sz, vector hotspot, vector rgb, float a, float f)
51 vector v1, v2, v3, v4;
53 hotspot = -1 * hotspot;
55 // hotspot-relative coordinates of the corners
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;
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;
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);
77 void drawquad(vector o, vector ri, vector up, string pic, vector rgb, float a, float f)
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);
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)
90 float owidth; // outer width
92 hotspot = -1 * hotspot;
94 // hotspot-relative coordinates of the healthbar corners
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);
104 owidth = width + 2 * border;
105 o = o - up * (margin + border + height) + ri * (sz_x - owidth) * 0.5;
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);
114 // returns location of sprite text
115 vector drawspritearrow(vector o, float ang, vector rgb, float a, float t)
117 float size = 9.0 * t;
118 float border = 1.5 * t;
119 float margin = 4.0 * t;
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);
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);
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);
141 return o + rotate(eY * (borderDiag+size+margin), ang);
144 // returns location of sprite healthbar
145 vector drawspritetext(vector o, float ang, float minwidth, vector rgb, float a, vector fontsize, string s)
149 float aspect, sa, ca;
151 sw = stringwidth(s, FALSE, fontsize);
158 // how do corners work?
159 aspect = vid_conwidth / vid_conheight;
161 ca = cos(ang) * aspect;
162 if(fabs(sa) > fabs(ca))
165 algny = 0.5 - 0.5 * ca / fabs(sa);
169 algnx = 0.5 - 0.5 * sa / fabs(ca);
177 // we want to be onscreen
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;
187 o_x += 0.5 * (w - sw);
189 drawstring(o, s, fontsize, rgb, a, DRAWFLAG_NORMAL);
197 float spritelookupblinkvalue(string s)
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;
214 vector spritelookupcolor(string s, vector def)
216 if(substring(s, 0, 4) == "wpn-")
217 return (get_weaponinfo(stof(substring(s, 4, strlen(s)))).wpcolor);
221 case "keycarrier-friend": return '0 1 0';
225 string spritelookuptext(string s)
227 if(substring(s, 0, 4) == "wpn-") { return (get_weaponinfo(stof(substring(s, 4, strlen(s)))).message); }
228 if(substring(s, 0, 5) == "buff-") { return Buff_PrettyName(Buff_Type_FromSprite(s)); }
232 case "as-push": return _("Push");
233 case "as-destroy": return _("Destroy");
234 case "as-defend": return _("Defend");
235 case "bluebase": return _("Blue base");
236 case "danger": return _("DANGER");
237 case "enemyflagcarrier": return _("Enemy carrier");
238 case "flagcarrier": return _("Flag carrier");
239 case "flagdropped": return _("Dropped flag");
240 case "helpme": return _("Help me!");
241 case "here": return _("Here");
242 case "key-dropped": return _("Dropped key");
243 case "keycarrier-blue": return _("Key carrier");
244 case "keycarrier-finish": return _("Run here");
245 case "keycarrier-friend": return _("Key carrier");
246 case "keycarrier-pink": return _("Key carrier");
247 case "keycarrier-red": return _("Key carrier");
248 case "keycarrier-yellow": return _("Key carrier");
249 case "redbase": return _("Red base");
250 case "waypoint": return _("Waypoint");
251 case "ons-gen": return _("Generator");
252 case "ons-gen-shielded": return _("Generator");
253 case "ons-cp": return _("Control point");
254 case "ons-cp-atck": return _("Control point");
255 case "ons-cp-dfnd": return _("Control point");
256 case "race-checkpoint": return _("Checkpoint");
257 case "race-finish": return _("Finish");
258 case "race-start": return _("Start");
259 case "race-start-finish": return (race_checkpointtime || race_mycheckpointtime) ? _("Finish") : _("Start");
260 case "goal": return _("Goal");
261 case "nb-ball": return _("Ball");
262 case "ka-ball": return _("Ball");
263 case "ka-ballcarrier": return _("Ball carrier");
264 case "dom-neut": return _("Control point");
265 case "dom-red": return _("Control point");
266 case "dom-blue": return _("Control point");
267 case "dom-yellow": return _("Control point");
268 case "dom-pink": return _("Control point");
269 case "item-invis": return _("Invisibility");
270 case "item-extralife": return _("Extra life");
271 case "item-speed": return _("Speed");
272 case "item-strength": return _("Strength");
273 case "item-shield": return _("Shield");
274 case "item-fuelregen": return _("Fuel regen");
275 case "item-jetpack": return _("Jet Pack");
276 case "frozen": return _("Frozen!");
277 case "tagged-target": return _("Tagged");
278 case "vehicle": return _("Vehicle");
283 vector fixrgbexcess_move(vector rgb, vector src, vector dst)
285 vector yvec = '0.299 0.587 0.114';
286 return rgb + dst * ((src * yvec) / (dst * yvec)) * ((rgb - '1 1 1') * src);
288 vector fixrgbexcess(vector rgb)
292 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 1 1');
295 rgb = fixrgbexcess_move(rgb, '0 1 0', '0 0 1');
301 rgb = fixrgbexcess_move(rgb, '0 0 1', '0 1 0');
308 rgb = fixrgbexcess_move(rgb, '0 1 0', '1 0 1');
311 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 0 1');
317 rgb = fixrgbexcess_move(rgb, '0 0 1', '1 0 0');
324 rgb = fixrgbexcess_move(rgb, '0 0 1', '1 1 0');
327 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 1 0');
333 rgb = fixrgbexcess_move(rgb, '0 1 0', '1 0 0');
341 float waypointsprite_count, waypointsprite_newcount;
342 void Draw_WaypointSprite()
348 self.alpha = pow(bound(0, (self.fadetime - time) / self.lifetime, 1), waypointsprite_timealphaexponent);
352 if(self.hideflags & 2)
353 return; // radar only
355 if(autocvar_cl_hidewaypoints >= 2)
358 if(self.hideflags & 1)
359 if(autocvar_cl_hidewaypoints)
360 return; // fixed waypoint
362 InterpolateOrigin_Do();
364 t = GetPlayerColor(player_localnum) + 1;
371 case SPRITERULE_DEFAULT:
375 spriteimage = self.netname;
380 spriteimage = self.netname;
382 case SPRITERULE_TEAMPLAY:
383 if(t == NUM_SPECTATOR + 1)
384 spriteimage = self.netname3;
385 else if(self.team == t)
386 spriteimage = self.netname2;
388 spriteimage = self.netname;
391 error("Invalid waypointsprite rule!");
395 if(spriteimage == "")
398 ++waypointsprite_newcount;
401 dist = vlen(self.origin - view_origin);
404 a = self.alpha * autocvar_hud_panel_fg_alpha;
406 if(self.maxdistance > waypointsprite_normdistance)
407 a *= pow(bound(0, (self.maxdistance - dist) / (self.maxdistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent);
408 else if(self.maxdistance > 0)
409 a *= pow(bound(0, (waypointsprite_fadedistance - dist) / (waypointsprite_fadedistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent) * (1 - waypointsprite_minalpha) + waypointsprite_minalpha;
412 rgb = self.teamradar_color;
413 rgb = spritelookupcolor(spriteimage, rgb);
416 self.teamradar_color = '1 0 1';
417 printf("WARNING: sprite of name %s has no color, using pink so you notice it\n", spriteimage);
420 if(time - floor(time) > 0.5)
422 if(self.helpme && time < self.helpme)
423 a *= SPRITE_HELPME_BLINK;
425 a *= spritelookupblinkvalue(spriteimage);
437 rgb = fixrgbexcess(rgb);
442 o = project_3d_to_2d(self.origin);
444 || o_x < (vid_conwidth * waypointsprite_edgeoffset_left)
445 || o_y < (vid_conheight * waypointsprite_edgeoffset_top)
446 || o_x > (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right))
447 || o_y > (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom)))
449 // scale it to be just in view
453 d = o - '0.5 0 0' * vid_conwidth - '0 0.5 0' * vid_conheight;
454 ang = atan2(-d_x, -d_y);
458 f1 = d_x / vid_conwidth;
459 f2 = d_y / vid_conheight;
461 if(max(f1, -f1) > max(f2, -f2))
466 d = d * ((0.5 - waypointsprite_edgeoffset_right) / f1);
471 d = d * (-(0.5 - waypointsprite_edgeoffset_left) / f1);
479 d = d * ((0.5 - waypointsprite_edgeoffset_bottom) / f2);
484 d = d * (-(0.5 - waypointsprite_edgeoffset_top) / f2);
488 o = d + '0.5 0 0' * vid_conwidth + '0 0.5 0' * vid_conheight;
496 d = o - '0.5 0 0' * vid_conwidth - '0 0.5 0' * vid_conheight;
497 ang = atan2(-d_x, -d_y);
502 float edgedistance_min, crosshairdistance;
503 edgedistance_min = min((o_y - (vid_conheight * waypointsprite_edgeoffset_top)),
504 (o_x - (vid_conwidth * waypointsprite_edgeoffset_left)),
505 (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right)) - o_x,
506 (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom)) - o_y);
509 vidscale = max(vid_conwidth / vid_width, vid_conheight / vid_height);
511 crosshairdistance = sqrt( pow(o_x - vid_conwidth/2, 2) + pow(o_y - vid_conheight/2, 2) );
513 t = waypointsprite_scale * vidscale;
514 a *= waypointsprite_alpha;
517 a = a * (1 - (1 - waypointsprite_distancefadealpha) * (bound(0, dist/waypointsprite_distancefadedistance, 1)));
518 t = t * (1 - (1 - waypointsprite_distancefadescale) * (bound(0, dist/waypointsprite_distancefadedistance, 1)));
520 if (edgedistance_min < waypointsprite_edgefadedistance) {
521 a = a * (1 - (1 - waypointsprite_edgefadealpha) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1)));
522 t = t * (1 - (1 - waypointsprite_edgefadescale) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1)));
524 if(crosshairdistance < waypointsprite_crosshairfadedistance) {
525 a = a * (1 - (1 - waypointsprite_crosshairfadealpha) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1)));
526 t = t * (1 - (1 - waypointsprite_crosshairfadescale) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1)));
529 if(self.build_finished)
531 if(time < self.build_finished + 0.25)
533 if(time < self.build_started)
534 self.health = self.build_starthealth;
535 else if(time < self.build_finished)
536 self.health = (time - self.build_started) / (self.build_finished - self.build_started) * (1 - self.build_starthealth) + self.build_starthealth;
544 o = drawspritearrow(o, ang, rgb, a, SPRITE_ARROW_SCALE * t);
547 if(autocvar_g_waypointsprite_spam && waypointsprite_count >= autocvar_g_waypointsprite_spam)
550 txt = spritelookuptext(spriteimage);
551 if(self.helpme && time < self.helpme)
552 txt = sprintf(_("%s needing help!"), txt);
553 if(autocvar_g_waypointsprite_uppercase)
554 txt = strtoupper(txt);
556 draw_beginBoldFont();
559 o = drawspritetext(o, ang, (SPRITE_HEALTHBAR_WIDTH + 2 * SPRITE_HEALTHBAR_BORDER) * t, rgb, a, waypointsprite_fontsize * '1 1 0', txt);
562 if(self.build_finished)
567 marg = -(SPRITE_HEALTHBAR_MARGIN + SPRITE_HEALTHBAR_HEIGHT + 2 * SPRITE_HEALTHBAR_BORDER) * t - 0.5 * waypointsprite_fontsize;
569 marg = SPRITE_HEALTHBAR_MARGIN * t + 0.5 * waypointsprite_fontsize;
576 SPRITE_HEALTHBAR_WIDTH * t,
577 SPRITE_HEALTHBAR_HEIGHT * t,
579 SPRITE_HEALTHBAR_BORDER * t,
582 a * SPRITE_HEALTHBAR_BORDERALPHA,
584 a * SPRITE_HEALTHBAR_HEALTHALPHA,
590 o = drawspritetext(o, ang, 0, rgb, a, waypointsprite_fontsize * '1 1 0', txt);
595 void Ent_RemoveWaypointSprite()
598 strunzone(self.netname);
600 strunzone(self.netname2);
602 strunzone(self.netname3);
605 void Ent_WaypointSprite()
607 float sendflags, f, t;
608 sendflags = ReadByte();
611 self.spawntime = time;
613 self.draw2d = Draw_WaypointSprite;
615 InterpolateOrigin_Undo();
616 self.iflags |= IFLAG_ORIGIN;
623 self.health = t / 191.0;
624 self.build_finished = 0;
628 t = (t - 192) * 256 + ReadByte();
629 self.build_started = servertime;
630 if(self.build_finished)
631 self.build_starthealth = bound(0, self.health, 1);
633 self.build_starthealth = 0;
634 self.build_finished = servertime + t / 32;
640 self.build_finished = 0;
645 // unfortunately, this needs to be exact (for the 3D display)
646 self.origin_x = ReadCoord();
647 self.origin_y = ReadCoord();
648 self.origin_z = ReadCoord();
649 setorigin(self, self.origin);
654 self.team = ReadByte();
655 self.rule = ReadByte();
661 strunzone(self.netname);
662 self.netname = strzone(ReadString());
668 strunzone(self.netname2);
669 self.netname2 = strzone(ReadString());
675 strunzone(self.netname3);
676 self.netname3 = strzone(ReadString());
681 self.lifetime = ReadCoord();
682 self.fadetime = ReadCoord();
683 self.maxdistance = ReadShort();
684 self.hideflags = ReadByte();
690 self.teamradar_icon = (f & 0x7F);
693 self.(teamradar_times[self.teamradar_time_index]) = time;
694 self.teamradar_time_index = (self.teamradar_time_index + 1) % MAX_TEAMRADAR_TIMES;
696 self.teamradar_color_x = ReadByte() / 255.0;
697 self.teamradar_color_y = ReadByte() / 255.0;
698 self.teamradar_color_z = ReadByte() / 255.0;
699 self.helpme = ReadByte() * 0.1;
701 self.helpme += servertime;
704 InterpolateOrigin_Note();
706 self.entremove = Ent_RemoveWaypointSprite;
709 void WaypointSprite_Load_Frames(string ext)
711 float dh, n, i, o, f;
712 string s, sname, sframes;
713 dh = search_begin(strcat("models/sprites/*_frame*", ext), FALSE, FALSE);
716 float ext_len = strlen(ext);
717 n = search_getsize(dh);
718 for(i = 0; i < n; ++i)
720 s = search_getfilename(dh, i);
721 s = substring(s, 15, strlen(s) - 15 - ext_len); // strip models/sprites/ and extension
723 o = strstrofs(s, "_frame", 0);
724 sname = strcat("/spriteframes/", substring(s, 0, o));
725 sframes = substring(s, o + 6, strlen(s) - o - 6);
726 f = stof(sframes) + 1;
727 db_put(tempdb, sname, ftos(max(f, stof(db_get(tempdb, sname)))));
732 void WaypointSprite_Load()
734 waypointsprite_fadedistance = vlen(mi_scale);
735 waypointsprite_normdistance = autocvar_g_waypointsprite_normdistance;
736 waypointsprite_minscale = autocvar_g_waypointsprite_minscale;
737 waypointsprite_minalpha = autocvar_g_waypointsprite_minalpha;
738 waypointsprite_distancealphaexponent = autocvar_g_waypointsprite_distancealphaexponent;
739 waypointsprite_timealphaexponent = autocvar_g_waypointsprite_timealphaexponent;
740 waypointsprite_scale = autocvar_g_waypointsprite_scale;
741 waypointsprite_fontsize = autocvar_g_waypointsprite_fontsize;
742 waypointsprite_edgefadealpha = autocvar_g_waypointsprite_edgefadealpha;
743 waypointsprite_edgefadescale = autocvar_g_waypointsprite_edgefadescale;
744 waypointsprite_edgefadedistance = autocvar_g_waypointsprite_edgefadedistance;
745 waypointsprite_edgeoffset_bottom = autocvar_g_waypointsprite_edgeoffset_bottom;
746 waypointsprite_edgeoffset_left = autocvar_g_waypointsprite_edgeoffset_left;
747 waypointsprite_edgeoffset_right = autocvar_g_waypointsprite_edgeoffset_right;
748 waypointsprite_edgeoffset_top = autocvar_g_waypointsprite_edgeoffset_top;
749 waypointsprite_crosshairfadealpha = autocvar_g_waypointsprite_crosshairfadealpha;
750 waypointsprite_crosshairfadescale = autocvar_g_waypointsprite_crosshairfadescale;
751 waypointsprite_crosshairfadedistance = autocvar_g_waypointsprite_crosshairfadedistance;
752 waypointsprite_distancefadealpha = autocvar_g_waypointsprite_distancefadealpha;
753 waypointsprite_distancefadescale = autocvar_g_waypointsprite_distancefadescale;
754 waypointsprite_distancefadedistance = waypointsprite_fadedistance * autocvar_g_waypointsprite_distancefadedistancemultiplier;
755 waypointsprite_alpha = autocvar_g_waypointsprite_alpha * (1 - autocvar__menu_alpha);
757 if(!waypointsprite_initialized)
759 WaypointSprite_Load_Frames(".tga");
760 WaypointSprite_Load_Frames(".jpg");
761 waypointsprite_initialized = true;
764 waypointsprite_count = waypointsprite_newcount;
765 waypointsprite_newcount = 0;