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)
113 float BORDER; BORDER = 1.5 * t;
114 float TSIZE; TSIZE = 8 * t;
115 float RLENGTH; RLENGTH = 8 * t;
116 float RWIDTH; RWIDTH = 4 * t;
117 float MLENGTH; MLENGTH = 4 * t;
119 R_BeginPolygon("", DRAWFLAG_NORMAL);
120 R_PolygonVertex(o + rotate(eX * -(TSIZE + BORDER * (1 + SQRT2)) + eY * (TSIZE + BORDER), ang), '0 0 0', '0 0 0', a);
121 R_PolygonVertex(o + rotate(eX * (TSIZE + BORDER * (1 + SQRT2)) + eY * (TSIZE + BORDER), ang), '0 0 0', '0 0 0', a);
122 R_PolygonVertex(o + rotate(eY * -( BORDER * SQRT2), ang), '0 0 0', '0 0 0', a);
124 R_BeginPolygon("", DRAWFLAG_NORMAL);
125 R_PolygonVertex(o + rotate(eX * -(RWIDTH + BORDER) + eY * (TSIZE + BORDER), ang), '0 0 0', '0 0 0', a);
126 R_PolygonVertex(o + rotate(eX * -(RWIDTH + BORDER) + eY * (TSIZE + RLENGTH + BORDER), ang), '0 0 0', '0 0 0', a);
127 R_PolygonVertex(o + rotate(eX * (RWIDTH + BORDER) + eY * (TSIZE + RLENGTH + BORDER), ang), '0 0 0', '0 0 0', a);
128 R_PolygonVertex(o + rotate(eX * (RWIDTH + BORDER) + eY * (TSIZE + BORDER), ang), '0 0 0', '0 0 0', a);
131 R_BeginPolygon("", DRAWFLAG_ADDITIVE);
132 R_PolygonVertex(o + rotate(eX * -TSIZE + eY * TSIZE, ang), '0 0 0', rgb, a);
133 R_PolygonVertex(o + rotate(eX * TSIZE + eY * TSIZE, ang), '0 0 0', rgb, a);
134 R_PolygonVertex(o + rotate('0 0 0', ang), '0 0 0', rgb, a);
136 R_BeginPolygon("", DRAWFLAG_ADDITIVE);
137 R_PolygonVertex(o + rotate(eX * -RWIDTH + eY * TSIZE, ang), '0 0 0', rgb, a);
138 R_PolygonVertex(o + rotate(eX * -RWIDTH + eY * (TSIZE + RLENGTH), ang), '0 0 0', rgb, a);
139 R_PolygonVertex(o + rotate(eX * RWIDTH + eY * (TSIZE + RLENGTH), ang), '0 0 0', rgb, a);
140 R_PolygonVertex(o + rotate(eX * RWIDTH + eY * TSIZE, ang), '0 0 0', rgb, a);
144 o + rotate(eY * (TSIZE + RLENGTH + MLENGTH), ang);
147 // returns location of sprite healthbar
148 vector drawspritetext(vector o, float ang, float minwidth, vector rgb, float a, vector fontsize, string s)
152 float aspect, sa, ca;
154 sw = stringwidth(s, FALSE, fontsize);
161 // how do corners work?
162 aspect = vid_conwidth / vid_conheight;
164 ca = cos(ang) * aspect;
165 if(fabs(sa) > fabs(ca))
168 algny = 0.5 - 0.5 * ca / fabs(sa);
172 algnx = 0.5 - 0.5 * sa / fabs(ca);
180 // we want to be onscreen
185 if(o_x > vid_conwidth - w)
186 o_x = vid_conwidth - w;
187 if(o_y > vid_conheight - h)
188 o_x = vid_conheight - h;
190 o_x += 0.5 * (w - sw);
192 drawstring(o, s, fontsize, rgb, a, DRAWFLAG_NORMAL);
200 float spritelookupblinkvalue(string s)
204 case "ons-cp-atck-neut": return 2;
205 case "ons-cp-atck-red": return 2;
206 case "ons-cp-atck-blue": return 2;
207 case "ons-cp-dfnd-red": return 0.5;
208 case "ons-cp-dfnd-blue": return 0.5;
209 case "item-invis": return 2;
210 case "item-extralife": return 2;
211 case "item-speed": return 2;
212 case "item-strength": return 2;
213 case "item-shueld": return 2;
214 case "item-fuelregen": return 2;
215 case "item-jetpack": return 2;
219 vector spritelookupcolor(string s, vector def)
223 case "keycarrier-friend": return '0 1 0';
227 string spritelookuptext(string s)
231 case "as-push": return _("Push");
232 case "as-destroy": return _("Destroy");
233 case "as-defend": return _("Defend");
234 case "bluebase": return _("Blue base");
235 case "danger": return _("DANGER");
236 case "flagcarrier": return _("Flag carrier");
237 case "flagdropped": return _("Dropped flag");
238 case "helpme": return _("Help me!");
239 case "here": return _("Here");
240 case "key-dropped": return _("Dropped key");
241 case "keycarrier-blue": return _("Key carrier");
242 case "keycarrier-finish": return _("Run here");
243 case "keycarrier-friend": return _("Key carrier");
244 case "keycarrier-pink": return _("Key carrier");
245 case "keycarrier-red": return _("Key carrier");
246 case "keycarrier-yellow": return _("Key carrier");
247 case "redbase": return _("Red base");
248 case "waypoint": return _("Waypoint");
249 case "ons-gen-red": return _("Generator");
250 case "ons-gen-blue": return _("Generator");
251 case "ons-gen-shielded": return _("Generator");
252 case "ons-cp-neut": return _("Control point");
253 case "ons-cp-red": return _("Control point");
254 case "ons-cp-blue": return _("Control point");
255 case "ons-cp-atck-neut": return _("Control point");
256 case "ons-cp-atck-red": return _("Control point");
257 case "ons-cp-atck-blue": return _("Control point");
258 case "ons-cp-dfnd-red": return _("Control point");
259 case "ons-cp-dfnd-blue": return _("Control point");
260 case "race-checkpoint": return _("Checkpoint");
261 case "race-finish": return _("Finish");
262 case "race-start": return _("Start");
263 case "nb-ball": return _("Ball");
264 case "ka-ball": return _("Ball");
265 case "ka-ballcarrier": return _("Ball carrier");
266 case "wpn-laser": return _("Laser");
267 case "wpn-shotgun": return _("Shotgun");
268 case "wpn-uzi": return _("Machine Gun");
269 case "wpn-gl": return _("Mortar");
270 case "wpn-electro": return _("Electro");
271 case "wpn-crylink": return _("Crylink");
272 case "wpn-nex": return _("Nex");
273 case "wpn-hagar": return _("Hagar");
274 case "wpn-rl": return _("Rocket Launcher");
275 case "wpn-porto": return _("Port-O-Launch");
276 case "wpn-minstanex": return _("Minstanex");
277 case "wpn-hookgun": return _("Hook");
278 case "wpn-fireball": return _("Fireball");
279 case "wpn-hlac": return _("HLAC");
280 case "wpn-campingrifle": return _("Rifle");
281 case "wpn-minelayer": return _("Mine Layer");
282 case "dom-neut": return _("Control point");
283 case "dom-red": return _("Control point");
284 case "dom-blue": return _("Control point");
285 case "dom-yellow": return _("Control point");
286 case "dom-pink": return _("Control point");
287 case "item-invis": return _("Invisibility");
288 case "item-extralife": return _("Extra life");
289 case "item-speed": return _("Speed");
290 case "item-strength": return _("Strength");
291 case "item-shield": return _("Shield");
292 case "item-fuelregen": return _("Fuel regen");
293 case "item-jetpack": return _("Jet Pack");
294 case "freezetag_frozen": return _("Frozen!");
295 case "tagged-target": return _("Tagged");
300 vector fixrgbexcess_move(vector rgb, vector src, vector dst)
302 vector yvec = '0.299 0.587 0.114';
303 return rgb + dst * ((src * yvec) / (dst * yvec)) * ((rgb - '1 1 1') * src);
305 vector fixrgbexcess(vector rgb)
309 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 1 1');
312 rgb = fixrgbexcess_move(rgb, '0 1 0', '0 0 1');
318 rgb = fixrgbexcess_move(rgb, '0 0 1', '0 1 0');
325 rgb = fixrgbexcess_move(rgb, '0 1 0', '1 0 1');
328 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 0 1');
334 rgb = fixrgbexcess_move(rgb, '0 0 1', '1 0 0');
341 rgb = fixrgbexcess_move(rgb, '0 0 1', '1 1 0');
344 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 1 0');
350 rgb = fixrgbexcess_move(rgb, '0 1 0', '1 0 0');
358 void Draw_WaypointSprite()
364 self.alpha = pow(bound(0, (self.fadetime - time) / self.lifetime, 1), waypointsprite_timealphaexponent);
368 if(self.hideflags & 2)
369 return; // radar only
371 if(autocvar_cl_hidewaypoints >= 2)
374 if(self.hideflags & 1)
375 if(autocvar_cl_hidewaypoints)
376 return; // fixed waypoint
378 InterpolateOrigin_Do();
380 t = GetPlayerColor(player_localentnum - 1) + 1;
387 case SPRITERULE_DEFAULT:
391 spriteimage = self.netname;
396 spriteimage = self.netname;
398 case SPRITERULE_TEAMPLAY:
399 if(t == COLOR_SPECTATOR + 1)
400 spriteimage = self.netname3;
401 else if(self.team == t)
402 spriteimage = self.netname2;
404 spriteimage = self.netname;
407 error("Invalid waypointsprite rule!");
411 if(spriteimage == "")
415 dist = vlen(self.origin - view_origin);
418 a = self.alpha * autocvar_hud_panel_fg_alpha;
420 if(self.maxdistance > waypointsprite_normdistance)
421 a *= pow(bound(0, (self.maxdistance - dist) / (self.maxdistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent);
422 else if(self.maxdistance > 0)
423 a *= pow(bound(0, (waypointsprite_fadedistance - dist) / (waypointsprite_fadedistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent) * (1 - waypointsprite_minalpha) + waypointsprite_minalpha;
426 rgb = self.teamradar_color;
427 rgb = spritelookupcolor(spriteimage, rgb);
429 if(time - floor(time) > 0.5)
430 a *= spritelookupblinkvalue(spriteimage);
441 rgb = fixrgbexcess(rgb);
446 o = project_3d_to_2d(self.origin);
447 if(o_z < 0 || o_x < 0 || o_y < 0 || o_x > vid_conwidth || o_y > vid_conheight)
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))
488 o = d + '0.5 0 0' * vid_conwidth + '0 0.5 0' * vid_conheight;
496 float edgedistance_min, crosshairdistance;
497 edgedistance_min = min4(o_y, o_x,vid_conwidth - o_x, vid_conheight - o_y);
500 vidscale = max(vid_conwidth / vid_width, vid_conheight / vid_height);
502 crosshairdistance = sqrt( pow(o_x - vid_conwidth/2, 2) + pow(o_y - vid_conheight/2, 2) );
504 t = waypointsprite_scale * vidscale;
505 a *= waypointsprite_alpha;
508 a = a * (1 - (1 - waypointsprite_distancefadealpha) * (bound(0, dist/waypointsprite_distancefadedistance, 1)));
509 t = t * (1 - (1 - waypointsprite_distancefadescale) * (bound(0, dist/waypointsprite_distancefadedistance, 1)));
511 if (edgedistance_min < waypointsprite_edgefadedistance) {
512 a = a * (1 - (1 - waypointsprite_edgefadealpha) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1)));
513 t = t * (1 - (1 - waypointsprite_edgefadescale) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1)));
515 if(crosshairdistance < waypointsprite_crosshairfadedistance) {
516 a = a * (1 - (1 - waypointsprite_crosshairfadealpha) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1)));
517 t = t * (1 - (1 - waypointsprite_crosshairfadescale) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1)));
520 if(self.build_finished)
522 if(time < self.build_finished + 0.25)
524 if(time < self.build_started)
525 self.health = self.build_starthealth;
526 else if(time < self.build_finished)
527 self.health = (time - self.build_started) / (self.build_finished - self.build_started) * (1 - self.build_starthealth) + self.build_starthealth;
535 o = drawspritearrow(o, ang, rgb, a, SPRITE_ARROW_SCALE * t);
538 txt = spritelookuptext(spriteimage);
539 if(autocvar_g_waypointsprite_uppercase)
540 txt = strtoupper(txt);
544 o = drawspritetext(o, ang, (SPRITE_HEALTHBAR_WIDTH + 2 * SPRITE_HEALTHBAR_BORDER) * t, rgb, a, waypointsprite_fontsize * '1 1 0', txt);
547 if(self.build_finished)
552 marg = -(SPRITE_HEALTHBAR_MARGIN + SPRITE_HEALTHBAR_HEIGHT + 2 * SPRITE_HEALTHBAR_BORDER) * t - 0.5 * waypointsprite_fontsize;
554 marg = SPRITE_HEALTHBAR_MARGIN * t + 0.5 * waypointsprite_fontsize;
561 SPRITE_HEALTHBAR_WIDTH * t,
562 SPRITE_HEALTHBAR_HEIGHT * t,
564 SPRITE_HEALTHBAR_BORDER * t,
567 a * SPRITE_HEALTHBAR_BORDERALPHA,
569 a * SPRITE_HEALTHBAR_HEALTHALPHA,
575 o = drawspritetext(o, ang, 0, rgb, a, waypointsprite_fontsize * '1 1 0', txt);
579 void Ent_RemoveWaypointSprite()
582 strunzone(self.netname);
584 strunzone(self.netname2);
586 strunzone(self.netname3);
589 void Ent_WaypointSprite()
591 float sendflags, f, t;
592 sendflags = ReadByte();
595 self.spawntime = time;
597 self.draw2d = Draw_WaypointSprite;
599 InterpolateOrigin_Undo();
606 self.health = t / 191.0;
607 self.build_finished = 0;
611 t = (t - 192) * 256 + ReadByte();
612 self.build_started = servertime;
613 if(self.build_finished)
614 self.build_starthealth = bound(0, self.health, 1);
616 self.build_starthealth = 0;
617 self.build_finished = servertime + t / 32;
623 self.build_finished = 0;
628 // unfortunately, this needs to be exact (for the 3D display)
629 self.origin_x = ReadCoord();
630 self.origin_y = ReadCoord();
631 self.origin_z = ReadCoord();
636 self.team = ReadByte();
637 self.rule = ReadByte();
643 strunzone(self.netname);
644 self.netname = strzone(ReadString());
650 strunzone(self.netname2);
651 self.netname2 = strzone(ReadString());
657 strunzone(self.netname3);
658 self.netname3 = strzone(ReadString());
663 self.lifetime = ReadCoord();
664 self.fadetime = ReadCoord();
665 self.maxdistance = ReadShort();
666 self.hideflags = ReadByte();
672 self.teamradar_icon = (f & 0x7F);
675 self.(teamradar_times[self.teamradar_time_index]) = time;
676 self.teamradar_time_index = mod(self.teamradar_time_index + 1, MAX_TEAMRADAR_TIMES);
678 self.teamradar_color_x = ReadByte() / 255.0;
679 self.teamradar_color_y = ReadByte() / 255.0;
680 self.teamradar_color_z = ReadByte() / 255.0;
683 InterpolateOrigin_Note();
685 self.entremove = Ent_RemoveWaypointSprite;
688 void WaypointSprite_Load_Frames(string ext)
690 float dh, n, i, o, f;
691 string s, sname, sframes;
692 dh = search_begin(strcat("models/sprites/*_frame*", ext), FALSE, FALSE);
695 float ext_len = strlen(ext);
696 n = search_getsize(dh);
697 for(i = 0; i < n; ++i)
699 s = search_getfilename(dh, i);
700 s = substring(s, 15, strlen(s) - 15 - ext_len); // strip models/sprites/ and extension
702 o = strstrofs(s, "_frame", 0);
703 sname = strcat("/spriteframes/", substring(s, 0, o));
704 sframes = substring(s, o + 6, strlen(s) - o - 6);
705 f = stof(sframes) + 1;
706 db_put(tempdb, sname, ftos(max(f, stof(db_get(tempdb, sname)))));
711 void WaypointSprite_Load()
713 waypointsprite_fadedistance = vlen(mi_scale);
714 waypointsprite_normdistance = autocvar_g_waypointsprite_normdistance;
715 waypointsprite_minscale = autocvar_g_waypointsprite_minscale;
716 waypointsprite_minalpha = autocvar_g_waypointsprite_minalpha;
717 waypointsprite_distancealphaexponent = autocvar_g_waypointsprite_distancealphaexponent;
718 waypointsprite_timealphaexponent = autocvar_g_waypointsprite_timealphaexponent;
719 waypointsprite_scale = autocvar_g_waypointsprite_scale;
720 waypointsprite_fontsize = autocvar_g_waypointsprite_fontsize;
721 waypointsprite_edgefadealpha = autocvar_g_waypointsprite_edgefadealpha;
722 waypointsprite_edgefadescale = autocvar_g_waypointsprite_edgefadescale;
723 waypointsprite_edgefadedistance = autocvar_g_waypointsprite_edgefadedistance;
724 waypointsprite_crosshairfadealpha = autocvar_g_waypointsprite_crosshairfadealpha;
725 waypointsprite_crosshairfadescale = autocvar_g_waypointsprite_crosshairfadescale;
726 waypointsprite_crosshairfadedistance = autocvar_g_waypointsprite_crosshairfadedistance;
727 waypointsprite_distancefadealpha = autocvar_g_waypointsprite_distancefadealpha;
728 waypointsprite_distancefadescale = autocvar_g_waypointsprite_distancefadescale;
729 waypointsprite_distancefadedistance = waypointsprite_fadedistance * autocvar_g_waypointsprite_distancefadedistancemultiplier;
730 waypointsprite_alpha = autocvar_g_waypointsprite_alpha * (1 - autocvar__menu_alpha);
732 if(!waypointsprite_initialized)
734 WaypointSprite_Load_Frames(".tga");
735 WaypointSprite_Load_Frames(".jpg");
736 waypointsprite_initialized = true;