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 txt = strtoupper(txt);
527 o = drawspritetext(o, ang, (SPRITE_HEALTHBAR_WIDTH + 2 * SPRITE_HEALTHBAR_BORDER) * t, rgb, a, waypointsprite_fontsize * '1 1 0', txt);
530 if(self.build_finished)
535 marg = -(SPRITE_HEALTHBAR_MARGIN + SPRITE_HEALTHBAR_HEIGHT + 2 * SPRITE_HEALTHBAR_BORDER) * t - 0.5 * waypointsprite_fontsize;
537 marg = SPRITE_HEALTHBAR_MARGIN * t + 0.5 * waypointsprite_fontsize;
544 SPRITE_HEALTHBAR_WIDTH * t,
545 SPRITE_HEALTHBAR_HEIGHT * t,
547 SPRITE_HEALTHBAR_BORDER * t,
550 a * SPRITE_HEALTHBAR_BORDERALPHA,
552 a * SPRITE_HEALTHBAR_HEALTHALPHA,
558 o = drawspritetext(o, ang, 0, rgb, a, waypointsprite_fontsize * '1 1 0', txt);
562 void Ent_RemoveWaypointSprite()
565 strunzone(self.netname);
567 strunzone(self.netname2);
569 strunzone(self.netname3);
572 void Ent_WaypointSprite()
574 float sendflags, f, t;
575 sendflags = ReadByte();
578 self.spawntime = time;
580 self.draw2d = Draw_WaypointSprite;
582 InterpolateOrigin_Undo();
589 self.health = t / 191.0;
590 self.build_finished = 0;
594 t = (t - 192) * 256 + ReadByte();
595 self.build_started = servertime;
596 if(self.build_finished)
597 self.build_starthealth = bound(0, self.health, 1);
599 self.build_starthealth = 0;
600 self.build_finished = servertime + t / 32;
606 self.build_finished = 0;
611 // unfortunately, this needs to be exact (for the 3D display)
612 self.origin_x = ReadCoord();
613 self.origin_y = ReadCoord();
614 self.origin_z = ReadCoord();
619 self.team = ReadByte();
620 self.rule = ReadByte();
626 strunzone(self.netname);
627 self.netname = strzone(ReadString());
633 strunzone(self.netname2);
634 self.netname2 = strzone(ReadString());
640 strunzone(self.netname3);
641 self.netname3 = strzone(ReadString());
646 self.lifetime = ReadCoord();
647 self.fadetime = ReadCoord();
648 self.maxdistance = ReadShort();
649 self.hideflags = ReadByte();
655 self.teamradar_icon = (f & 0x7F);
658 self.(teamradar_times[self.teamradar_time_index]) = time;
659 self.teamradar_time_index = mod(self.teamradar_time_index + 1, MAX_TEAMRADAR_TIMES);
661 self.teamradar_color_x = ReadByte() / 255.0;
662 self.teamradar_color_y = ReadByte() / 255.0;
663 self.teamradar_color_z = ReadByte() / 255.0;
666 InterpolateOrigin_Note();
668 self.entremove = Ent_RemoveWaypointSprite;
671 void WaypointSprite_Load_Frames(string ext)
673 float dh, n, i, o, f;
674 string s, sname, sframes;
675 dh = search_begin(strcat("models/sprites/*_frame*", ext), FALSE, FALSE);
678 float ext_len = strlen(ext);
679 n = search_getsize(dh);
680 for(i = 0; i < n; ++i)
682 s = search_getfilename(dh, i);
683 s = substring(s, 15, strlen(s) - 15 - ext_len); // strip models/sprites/ and extension
685 o = strstrofs(s, "_frame", 0);
686 sname = strcat("/spriteframes/", substring(s, 0, o));
687 sframes = substring(s, o + 6, strlen(s) - o - 6);
688 f = stof(sframes) + 1;
689 db_put(tempdb, sname, ftos(max(f, stof(db_get(tempdb, sname)))));
694 void WaypointSprite_Load()
696 waypointsprite_fadedistance = vlen(mi_scale);
697 waypointsprite_normdistance = autocvar_g_waypointsprite_normdistance;
698 waypointsprite_minscale = autocvar_g_waypointsprite_minscale;
699 waypointsprite_minalpha = autocvar_g_waypointsprite_minalpha;
700 waypointsprite_distancealphaexponent = autocvar_g_waypointsprite_distancealphaexponent;
701 waypointsprite_timealphaexponent = autocvar_g_waypointsprite_timealphaexponent;
702 waypointsprite_scale = autocvar_g_waypointsprite_scale;
703 waypointsprite_fontsize = autocvar_g_waypointsprite_fontsize;
704 waypointsprite_edgefadealpha = autocvar_g_waypointsprite_edgefadealpha;
705 waypointsprite_edgefadescale = autocvar_g_waypointsprite_edgefadescale;
706 waypointsprite_edgefadedistance = autocvar_g_waypointsprite_edgefadedistance;
707 waypointsprite_crosshairfadealpha = autocvar_g_waypointsprite_crosshairfadealpha;
708 waypointsprite_crosshairfadescale = autocvar_g_waypointsprite_crosshairfadescale;
709 waypointsprite_crosshairfadedistance = autocvar_g_waypointsprite_crosshairfadedistance;
710 waypointsprite_distancefadealpha = autocvar_g_waypointsprite_distancefadealpha;
711 waypointsprite_distancefadescale = autocvar_g_waypointsprite_distancefadescale;
712 waypointsprite_distancefadedistance = waypointsprite_fadedistance * autocvar_g_waypointsprite_distancefadedistancemultiplier;
713 waypointsprite_alpha = autocvar_g_waypointsprite_alpha * (1 - autocvar__menu_alpha);
715 if(!waypointsprite_initialized)
717 WaypointSprite_Load_Frames(".tga");
718 WaypointSprite_Load_Frames(".jpg");
719 waypointsprite_initialized = true;