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_crosshairfadealpha;
14 float waypointsprite_crosshairfadescale;
15 float waypointsprite_crosshairfadedistance;
16 float waypointsprite_distancefadealpha;
17 float waypointsprite_distancefadescale;
18 float waypointsprite_distancefadedistance;
19 float waypointsprite_alpha;
22 .string netname; // primary picture
23 .string netname2; // secondary picture
24 .string netname3; // tertiary picture
25 .float team; // team that gets netname2
33 .float build_starthealth;
34 .float build_finished;
36 float SPRITE_HEALTHBAR_WIDTH = 144;
37 float SPRITE_HEALTHBAR_HEIGHT = 9;
38 float SPRITE_HEALTHBAR_MARGIN = 6;
39 float SPRITE_HEALTHBAR_BORDER = 2;
40 float SPRITE_HEALTHBAR_BORDERALPHA = 1;
41 float SPRITE_HEALTHBAR_HEALTHALPHA = 0.5;
42 float SPRITE_ARROW_SCALE = 1.0;
44 void drawrotpic(vector org, float rot, string pic, vector sz, vector hotspot, vector rgb, float a, float f)
46 vector v1, v2, v3, v4;
48 hotspot = -1 * hotspot;
50 // hotspot-relative coordinates of the corners
52 v2 = hotspot + '1 0 0' * sz_x;
53 v3 = hotspot + '1 0 0' * sz_x + '0 1 0' * sz_y;
54 v4 = hotspot + '0 1 0' * sz_y;
56 // rotate them, and make them absolute
57 rot = -rot; // rotate by the opposite angle, as our coordinate system is reversed
58 v1 = rotate(v1, rot) + org;
59 v2 = rotate(v2, rot) + org;
60 v3 = rotate(v3, rot) + org;
61 v4 = rotate(v4, rot) + org;
64 R_BeginPolygon(pic, f);
65 R_PolygonVertex(v1, '0 0 0', rgb, a);
66 R_PolygonVertex(v2, '1 0 0', rgb, a);
67 R_PolygonVertex(v3, '1 1 0', rgb, a);
68 R_PolygonVertex(v4, '0 1 0', rgb, a);
72 void drawquad(vector o, vector ri, vector up, string pic, vector rgb, float a, float f)
74 R_BeginPolygon(pic, f);
75 R_PolygonVertex(o, '0 0 0', rgb, a);
76 R_PolygonVertex(o + ri, '1 0 0', rgb, a);
77 R_PolygonVertex(o + up + ri, '1 1 0', rgb, a);
78 R_PolygonVertex(o + up, '0 1 0', rgb, a);
82 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)
85 float owidth; // outer width
87 hotspot = -1 * hotspot;
89 // hotspot-relative coordinates of the healthbar corners
94 rot = -rot; // rotate by the opposite angle, as our coordinate system is reversed
95 o = rotate(o, rot) + org;
99 owidth = width + 2 * border;
100 o = o - up * (margin + border + height) + ri * (sz_x - owidth) * 0.5;
102 drawquad(o - up * border, ri * owidth, up * border, "", rgb, a, f);
103 drawquad(o + up * height, ri * owidth, up * border, "", rgb, a, f);
104 drawquad(o, ri * border, up * height, "", rgb, a, f);
105 drawquad(o + ri * (owidth - border), ri * border, up * height, "", rgb, a, f);
106 drawquad(o + ri * (border + align * ((1 - h) * width)), ri * width * h, up * height, "", hrgb, ha, f);
109 // returns location of sprite text
110 vector drawspritearrow(vector o, float ang, vector rgb, float a, float t)
112 R_BeginPolygon("", DRAWFLAG_NORMAL);
113 R_PolygonVertex(o + rotate('-11.5 9.5 0'*t, ang), '0 0 0', '0 0 0', a);
114 R_PolygonVertex(o + rotate('11.5 9.5 0'*t, ang), '0 0 0', '0 0 0', a);
115 R_PolygonVertex(o + rotate('0 -2 0'*t, ang), '0 0 0', '0 0 0', a);
117 R_BeginPolygon("", DRAWFLAG_NORMAL);
118 R_PolygonVertex(o + rotate('-5.5 9.5 0'*t, ang), '0 0 0', '0 0 0', a);
119 R_PolygonVertex(o + rotate('-5.5 17.5 0'*t, ang), '0 0 0', '0 0 0', a);
120 R_PolygonVertex(o + rotate('5.5 17.5 0'*t, ang), '0 0 0', '0 0 0', a);
121 R_PolygonVertex(o + rotate('5.5 9.5 0'*t, ang), '0 0 0', '0 0 0', a);
124 R_BeginPolygon("", DRAWFLAG_NORMAL);
125 R_PolygonVertex(o + rotate('-8 8 0'*t, ang), '0 0 0', rgb, a);
126 R_PolygonVertex(o + rotate('8 8 0'*t, ang), '0 0 0', rgb, a);
127 R_PolygonVertex(o + rotate('0 0 0'*t, ang), '0 0 0', rgb, a);
129 R_BeginPolygon("", DRAWFLAG_NORMAL);
130 R_PolygonVertex(o + rotate('-4 8 0'*t, ang), '0 0 0', rgb, a);
131 R_PolygonVertex(o + rotate('-4 16 0'*t, ang), '0 0 0', rgb, a);
132 R_PolygonVertex(o + rotate('4 16 0'*t, ang), '0 0 0', rgb, a);
133 R_PolygonVertex(o + rotate('4 8 0'*t, ang), '0 0 0', rgb, a);
137 o + rotate('0 20 0'*t, ang);
140 // returns location of sprite healthbar
141 vector drawspritetext(vector o, float ang, float minwidth, vector rgb, float a, vector fontsize, string s)
145 float aspect, sa, ca;
147 sw = stringwidth(s, FALSE, fontsize);
154 // how do corners work?
155 aspect = vid_conwidth / vid_conheight;
157 ca = cos(ang) * aspect;
158 if(fabs(sa) > fabs(ca))
161 algny = 0.5 - 0.5 * ca / fabs(sa);
165 algnx = 0.5 - 0.5 * sa / fabs(ca);
173 // we want to be onscreen
178 if(o_x > vid_conwidth - w)
179 o_x = vid_conwidth - w;
180 if(o_y > vid_conheight - h)
181 o_x = vid_conheight - h;
183 o_x += 0.5 * (w - sw);
185 drawstring(o, s, fontsize, rgb, a, DRAWFLAG_NORMAL);
193 float spritelookupblinkvalue(string s)
197 case "ons-cp-atck-neut": return 2;
198 case "ons-cp-atck-red": return 2;
199 case "ons-cp-atck-blue": return 2;
200 case "ons-cp-dfnd-red": return 0.5;
201 case "ons-cp-dfnd-blue": return 0.5;
202 case "item-invis": return 2;
203 case "item-extralife": return 2;
204 case "item-speed": return 2;
205 case "item-strength": return 2;
206 case "item-shueld": return 2;
207 case "item-fuelregen": return 2;
208 case "item-jetpack": return 2;
212 string spritelookuptext(string s)
216 case "as-push": return _("Push");
217 case "as-destroy": return _("Destroy");
218 case "as-defend": return _("Defend");
219 case "bluebase": return _("Blue base");
220 case "danger": return _("DANGER");
221 case "flagcarrier": return _("Flag carrier");
222 case "flagdropped": return _("Dropped flag");
223 case "helpme": return _("Help me!");
224 case "here": return _("Here");
225 case "key-dropped": return _("Dropped key");
226 case "keycarrier-blue": return _("Key carrier");
227 case "keycarrier-finish": return _("Run here");
228 case "keycarrier-friend": return _("Key carrier");
229 case "keycarrier-pink": return _("Key carrier");
230 case "keycarrier-red": return _("Key carrier");
231 case "keycarrier-yellow": return _("Key carrier");
232 case "redbase": return _("Red base");
233 case "waypoint": return _("Waypoint");
234 case "ons-gen-red": return _("Generator");
235 case "ons-gen-blue": return _("Generator");
236 case "ons-gen-shielded": return _("Generator");
237 case "ons-cp-neut": return _("Control point");
238 case "ons-cp-red": return _("Control point");
239 case "ons-cp-blue": return _("Control point");
240 case "ons-cp-atck-neut": return _("Control point");
241 case "ons-cp-atck-red": return _("Control point");
242 case "ons-cp-atck-blue": return _("Control point");
243 case "ons-cp-dfnd-red": return _("Control point");
244 case "ons-cp-dfnd-blue": return _("Control point");
245 case "race-checkpoint": return _("Checkpoint");
246 case "race-finish": return _("Finish");
247 case "race-start": return _("Start");
248 case "nb-ball": return _("Ball");
249 case "ka-ball": return _("Ball");
250 case "ka-ballcarrier": return _("Ball carrier");
251 case "wpn-laser": return _("Laser");
252 case "wpn-shotgun": return _("Shotgun");
253 case "wpn-uzi": return _("Machine Gun");
254 case "wpn-gl": return _("Mortar");
255 case "wpn-electro": return _("Electro");
256 case "wpn-crylink": return _("Crylink");
257 case "wpn-nex": return _("Nex");
258 case "wpn-hagar": return _("Hagar");
259 case "wpn-rl": return _("Rocket Launcher");
260 case "wpn-porto": return _("Port-O-Launch");
261 case "wpn-minstanex": return _("Minstanex");
262 case "wpn-hookgun": return _("Hook");
263 case "wpn-fireball": return _("Fireball");
264 case "wpn-hlac": return _("HLAC");
265 case "wpn-campingrifle": return _("Rifle");
266 case "wpn-minelayer": return _("Mine Layer");
267 case "dom-neut": return _("Control point");
268 case "dom-red": return _("Control point");
269 case "dom-blue": return _("Control point");
270 case "dom-yellow": return _("Control point");
271 case "dom-pink": return _("Control point");
272 case "item-invis": return _("Invisibility");
273 case "item-extralife": return _("Extra life");
274 case "item-speed": return _("Speed");
275 case "item-strength": return _("Strength");
276 case "item-shield": return _("Shield");
277 case "item-fuelregen": return _("Fuel regen");
278 case "item-jetpack": return _("Jet Pack");
279 case "freezetag_frozen": return _("Frozen!");
280 case "tagged-target": return _("Tagged");
285 vector fixrgbexcess_move(vector rgb, vector src, vector dst)
287 vector yvec = '0.299 0.587 0.114';
288 return rgb + dst * ((src * yvec) / (dst * yvec)) * ((rgb - '1 1 1') * src);
290 vector fixrgbexcess(vector rgb)
294 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 1 1');
297 rgb = fixrgbexcess_move(rgb, '0 1 0', '0 0 1');
303 rgb = fixrgbexcess_move(rgb, '0 0 1', '0 1 0');
310 rgb = fixrgbexcess_move(rgb, '0 1 0', '1 0 1');
313 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 0 1');
319 rgb = fixrgbexcess_move(rgb, '0 0 1', '1 0 0');
326 rgb = fixrgbexcess_move(rgb, '0 0 1', '1 1 0');
329 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 1 0');
335 rgb = fixrgbexcess_move(rgb, '0 1 0', '1 0 0');
343 void Draw_WaypointSprite()
349 self.alpha = pow(bound(0, (self.fadetime - time) / self.lifetime, 1), waypointsprite_timealphaexponent);
353 if(self.hideflags & 2)
354 return; // radar only
356 if(autocvar_cl_hidewaypoints >= 2)
359 if(self.hideflags & 1)
360 if(autocvar_cl_hidewaypoints)
361 return; // fixed waypoint
363 InterpolateOrigin_Do();
365 t = GetPlayerColor(player_localentnum - 1) + 1;
372 case SPRITERULE_DEFAULT:
376 spriteimage = self.netname;
381 spriteimage = self.netname;
383 case SPRITERULE_TEAMPLAY:
384 if(t == COLOR_SPECTATOR + 1)
385 spriteimage = self.netname3;
386 else if(self.team == t)
387 spriteimage = self.netname2;
389 spriteimage = self.netname;
392 error("Invalid waypointsprite rule!");
396 if(spriteimage == "")
400 dist = vlen(self.origin - view_origin);
403 a = self.alpha * autocvar_hud_panel_fg_alpha;
405 if(self.maxdistance > waypointsprite_normdistance)
406 a *= pow(bound(0, (self.maxdistance - dist) / (self.maxdistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent);
407 else if(self.maxdistance > 0)
408 a *= pow(bound(0, (waypointsprite_fadedistance - dist) / (waypointsprite_fadedistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent) * (1 - waypointsprite_minalpha) + waypointsprite_minalpha;
411 rgb = self.teamradar_color;
413 if(time - floor(time) > 0.5)
414 a *= spritelookupblinkvalue(spriteimage);
425 rgb = fixrgbexcess(rgb);
430 o = project_3d_to_2d(self.origin);
431 if(o_z < 0 || o_x < 0 || o_y < 0 || o_x > vid_conwidth || o_y > vid_conheight)
433 // scale it to be just in view
437 d = o - '0.5 0 0' * vid_conwidth - '0 0.5 0' * vid_conheight;
438 ang = atan2(-d_x, -d_y);
442 f1 = d_x / vid_conwidth;
443 f2 = d_y / vid_conheight;
445 if(max(f1, -f1) > max(f2, -f2))
472 o = d + '0.5 0 0' * vid_conwidth + '0 0.5 0' * vid_conheight;
480 float edgedistance_min, crosshairdistance;
481 edgedistance_min = min4(o_y, o_x,vid_conwidth - o_x, vid_conheight - o_y);
484 vidscale = max(vid_conwidth / vid_width, vid_conheight / vid_height);
486 crosshairdistance = sqrt( pow(o_x - vid_conwidth/2, 2) + pow(o_y - vid_conheight/2, 2) );
488 t = waypointsprite_scale * vidscale;
489 a *= waypointsprite_alpha;
492 a = a * (1 - (1 - waypointsprite_distancefadealpha) * (bound(0, dist/waypointsprite_distancefadedistance, 1)));
493 t = t * (1 - (1 - waypointsprite_distancefadescale) * (bound(0, dist/waypointsprite_distancefadedistance, 1)));
495 if (edgedistance_min < waypointsprite_edgefadedistance) {
496 a = a * (1 - (1 - waypointsprite_edgefadealpha) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1)));
497 t = t * (1 - (1 - waypointsprite_edgefadescale) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1)));
499 if(crosshairdistance < waypointsprite_crosshairfadedistance) {
500 a = a * (1 - (1 - waypointsprite_crosshairfadealpha) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1)));
501 t = t * (1 - (1 - waypointsprite_crosshairfadescale) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1)));
504 if(self.build_finished)
506 if(time < self.build_finished + 0.25)
508 if(time < self.build_started)
509 self.health = self.build_starthealth;
510 else if(time < self.build_finished)
511 self.health = (time - self.build_started) / (self.build_finished - self.build_started) * (1 - self.build_starthealth) + self.build_starthealth;
519 o = drawspritearrow(o, ang, rgb, a, SPRITE_ARROW_SCALE * t);
522 txt = spritelookuptext(spriteimage);
523 if(autocvar_g_waypointsprite_uppercase)
524 txt = strtoupper(txt);
528 o = drawspritetext(o, ang, (SPRITE_HEALTHBAR_WIDTH + 2 * SPRITE_HEALTHBAR_BORDER) * t, rgb, a, waypointsprite_fontsize * '1 1 0', txt);
531 if(self.build_finished)
536 marg = -(SPRITE_HEALTHBAR_MARGIN + SPRITE_HEALTHBAR_HEIGHT + 2 * SPRITE_HEALTHBAR_BORDER) * t - 0.5 * waypointsprite_fontsize;
538 marg = SPRITE_HEALTHBAR_MARGIN * t + 0.5 * waypointsprite_fontsize;
545 SPRITE_HEALTHBAR_WIDTH * t,
546 SPRITE_HEALTHBAR_HEIGHT * t,
548 SPRITE_HEALTHBAR_BORDER * t,
551 a * SPRITE_HEALTHBAR_BORDERALPHA,
553 a * SPRITE_HEALTHBAR_HEALTHALPHA,
559 o = drawspritetext(o, ang, 0, rgb, a, waypointsprite_fontsize * '1 1 0', txt);
563 void Ent_RemoveWaypointSprite()
566 strunzone(self.netname);
568 strunzone(self.netname2);
570 strunzone(self.netname3);
573 void Ent_WaypointSprite()
575 float sendflags, f, t;
576 sendflags = ReadByte();
579 self.spawntime = time;
581 self.draw2d = Draw_WaypointSprite;
583 InterpolateOrigin_Undo();
590 self.health = t / 191.0;
591 self.build_finished = 0;
595 t = (t - 192) * 256 + ReadByte();
596 self.build_started = servertime;
597 if(self.build_finished)
598 self.build_starthealth = bound(0, self.health, 1);
600 self.build_starthealth = 0;
601 self.build_finished = servertime + t / 32;
607 self.build_finished = 0;
612 // unfortunately, this needs to be exact (for the 3D display)
613 self.origin_x = ReadCoord();
614 self.origin_y = ReadCoord();
615 self.origin_z = ReadCoord();
620 self.team = ReadByte();
621 self.rule = ReadByte();
627 strunzone(self.netname);
628 self.netname = strzone(ReadString());
634 strunzone(self.netname2);
635 self.netname2 = strzone(ReadString());
641 strunzone(self.netname3);
642 self.netname3 = strzone(ReadString());
647 self.lifetime = ReadCoord();
648 self.fadetime = ReadCoord();
649 self.maxdistance = ReadShort();
650 self.hideflags = ReadByte();
656 self.teamradar_icon = (f & 0x7F);
659 self.(teamradar_times[self.teamradar_time_index]) = time;
660 self.teamradar_time_index = mod(self.teamradar_time_index + 1, MAX_TEAMRADAR_TIMES);
662 self.teamradar_color_x = ReadByte() / 255.0;
663 self.teamradar_color_y = ReadByte() / 255.0;
664 self.teamradar_color_z = ReadByte() / 255.0;
667 InterpolateOrigin_Note();
669 self.entremove = Ent_RemoveWaypointSprite;
672 void WaypointSprite_Load_Frames(string ext)
674 float dh, n, i, o, f;
675 string s, sname, sframes;
676 dh = search_begin(strcat("models/sprites/*_frame*", ext), FALSE, FALSE);
679 float ext_len = strlen(ext);
680 n = search_getsize(dh);
681 for(i = 0; i < n; ++i)
683 s = search_getfilename(dh, i);
684 s = substring(s, 15, strlen(s) - 15 - ext_len); // strip models/sprites/ and extension
686 o = strstrofs(s, "_frame", 0);
687 sname = strcat("/spriteframes/", substring(s, 0, o));
688 sframes = substring(s, o + 6, strlen(s) - o - 6);
689 f = stof(sframes) + 1;
690 db_put(tempdb, sname, ftos(max(f, stof(db_get(tempdb, sname)))));
695 void WaypointSprite_Load()
697 waypointsprite_fadedistance = vlen(mi_scale);
698 waypointsprite_normdistance = autocvar_g_waypointsprite_normdistance;
699 waypointsprite_minscale = autocvar_g_waypointsprite_minscale;
700 waypointsprite_minalpha = autocvar_g_waypointsprite_minalpha;
701 waypointsprite_distancealphaexponent = autocvar_g_waypointsprite_distancealphaexponent;
702 waypointsprite_timealphaexponent = autocvar_g_waypointsprite_timealphaexponent;
703 waypointsprite_scale = autocvar_g_waypointsprite_scale;
704 waypointsprite_fontsize = autocvar_g_waypointsprite_fontsize;
705 waypointsprite_edgefadealpha = autocvar_g_waypointsprite_edgefadealpha;
706 waypointsprite_edgefadescale = autocvar_g_waypointsprite_edgefadescale;
707 waypointsprite_edgefadedistance = autocvar_g_waypointsprite_edgefadedistance;
708 waypointsprite_crosshairfadealpha = autocvar_g_waypointsprite_crosshairfadealpha;
709 waypointsprite_crosshairfadescale = autocvar_g_waypointsprite_crosshairfadescale;
710 waypointsprite_crosshairfadedistance = autocvar_g_waypointsprite_crosshairfadedistance;
711 waypointsprite_distancefadealpha = autocvar_g_waypointsprite_distancefadealpha;
712 waypointsprite_distancefadescale = autocvar_g_waypointsprite_distancefadescale;
713 waypointsprite_distancefadedistance = waypointsprite_fadedistance * autocvar_g_waypointsprite_distancefadedistancemultiplier;
714 waypointsprite_alpha = autocvar_g_waypointsprite_alpha * (1 - autocvar__menu_alpha);
716 if(!waypointsprite_initialized)
718 WaypointSprite_Load_Frames(".tga");
719 WaypointSprite_Load_Frames(".jpg");
720 waypointsprite_initialized = true;