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;
118 R_BeginPolygon("", DRAWFLAG_NORMAL);
119 R_PolygonVertex(o + rotate(eX * -(TSIZE + BORDER * (1 + SQRT2)) + eY * (TSIZE + BORDER), ang), '0 0 0', '0 0 0', a);
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(eY * -( BORDER * SQRT2), ang), '0 0 0', '0 0 0', a);
123 R_BeginPolygon("", DRAWFLAG_NORMAL);
124 R_PolygonVertex(o + rotate(eX * -(RWIDTH + BORDER) + eY * (TSIZE + BORDER), ang), '0 0 0', '0 0 0', a);
125 R_PolygonVertex(o + rotate(eX * -(RWIDTH + BORDER) + eY * (TSIZE + RLENGTH + 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 + BORDER), ang), '0 0 0', '0 0 0', a);
130 R_BeginPolygon("", DRAWFLAG_ADDITIVE);
131 R_PolygonVertex(o + rotate(eX * -TSIZE + eY * TSIZE, ang), '0 0 0', rgb, a);
132 R_PolygonVertex(o + rotate(eX * TSIZE + eY * TSIZE, ang), '0 0 0', rgb, a);
133 R_PolygonVertex(o + rotate('0 0 0', ang), '0 0 0', rgb, a);
135 R_BeginPolygon("", DRAWFLAG_ADDITIVE);
136 R_PolygonVertex(o + rotate(eX * -RWIDTH + eY * TSIZE, ang), '0 0 0', rgb, a);
137 R_PolygonVertex(o + rotate(eX * -RWIDTH + eY * (TSIZE + RLENGTH), 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, ang), '0 0 0', rgb, a);
143 o + rotate('0 20 0'*t, ang);
146 // returns location of sprite healthbar
147 vector drawspritetext(vector o, float ang, float minwidth, vector rgb, float a, vector fontsize, string s)
151 float aspect, sa, ca;
153 sw = stringwidth(s, FALSE, fontsize);
160 // how do corners work?
161 aspect = vid_conwidth / vid_conheight;
163 ca = cos(ang) * aspect;
164 if(fabs(sa) > fabs(ca))
167 algny = 0.5 - 0.5 * ca / fabs(sa);
171 algnx = 0.5 - 0.5 * sa / fabs(ca);
179 // we want to be onscreen
184 if(o_x > vid_conwidth - w)
185 o_x = vid_conwidth - w;
186 if(o_y > vid_conheight - h)
187 o_x = vid_conheight - h;
189 o_x += 0.5 * (w - sw);
191 drawstring(o, s, fontsize, rgb, a, DRAWFLAG_NORMAL);
199 float spritelookupblinkvalue(string s)
203 case "ons-cp-atck-neut": return 2;
204 case "ons-cp-atck-red": return 2;
205 case "ons-cp-atck-blue": return 2;
206 case "ons-cp-dfnd-red": return 0.5;
207 case "ons-cp-dfnd-blue": return 0.5;
208 case "item-invis": return 2;
209 case "item-extralife": return 2;
210 case "item-speed": return 2;
211 case "item-strength": return 2;
212 case "item-shueld": return 2;
213 case "item-fuelregen": return 2;
214 case "item-jetpack": return 2;
218 vector spritelookupcolor(string s, vector def)
222 case "keycarrier-friend": return '0 1 0';
226 string spritelookuptext(string s)
230 case "as-push": return _("Push");
231 case "as-destroy": return _("Destroy");
232 case "as-defend": return _("Defend");
233 case "bluebase": return _("Blue base");
234 case "danger": return _("DANGER");
235 case "flagcarrier": return _("Flag carrier");
236 case "flagdropped": return _("Dropped flag");
237 case "helpme": return _("Help me!");
238 case "here": return _("Here");
239 case "key-dropped": return _("Dropped key");
240 case "keycarrier-blue": return _("Key carrier");
241 case "keycarrier-finish": return _("Run here");
242 case "keycarrier-friend": return _("Key carrier");
243 case "keycarrier-pink": return _("Key carrier");
244 case "keycarrier-red": return _("Key carrier");
245 case "keycarrier-yellow": return _("Key carrier");
246 case "redbase": return _("Red base");
247 case "waypoint": return _("Waypoint");
248 case "ons-gen-red": return _("Generator");
249 case "ons-gen-blue": return _("Generator");
250 case "ons-gen-shielded": return _("Generator");
251 case "ons-cp-neut": return _("Control point");
252 case "ons-cp-red": return _("Control point");
253 case "ons-cp-blue": return _("Control point");
254 case "ons-cp-atck-neut": return _("Control point");
255 case "ons-cp-atck-red": return _("Control point");
256 case "ons-cp-atck-blue": return _("Control point");
257 case "ons-cp-dfnd-red": return _("Control point");
258 case "ons-cp-dfnd-blue": return _("Control point");
259 case "race-checkpoint": return _("Checkpoint");
260 case "race-finish": return _("Finish");
261 case "race-start": return _("Start");
262 case "nb-ball": return _("Ball");
263 case "ka-ball": return _("Ball");
264 case "ka-ballcarrier": return _("Ball carrier");
265 case "wpn-laser": return _("Laser");
266 case "wpn-shotgun": return _("Shotgun");
267 case "wpn-uzi": return _("Machine Gun");
268 case "wpn-gl": return _("Mortar");
269 case "wpn-electro": return _("Electro");
270 case "wpn-crylink": return _("Crylink");
271 case "wpn-nex": return _("Nex");
272 case "wpn-hagar": return _("Hagar");
273 case "wpn-rl": return _("Rocket Launcher");
274 case "wpn-porto": return _("Port-O-Launch");
275 case "wpn-minstanex": return _("Minstanex");
276 case "wpn-hookgun": return _("Hook");
277 case "wpn-fireball": return _("Fireball");
278 case "wpn-hlac": return _("HLAC");
279 case "wpn-campingrifle": return _("Rifle");
280 case "wpn-minelayer": return _("Mine Layer");
281 case "dom-neut": return _("Control point");
282 case "dom-red": return _("Control point");
283 case "dom-blue": return _("Control point");
284 case "dom-yellow": return _("Control point");
285 case "dom-pink": return _("Control point");
286 case "item-invis": return _("Invisibility");
287 case "item-extralife": return _("Extra life");
288 case "item-speed": return _("Speed");
289 case "item-strength": return _("Strength");
290 case "item-shield": return _("Shield");
291 case "item-fuelregen": return _("Fuel regen");
292 case "item-jetpack": return _("Jet Pack");
293 case "freezetag_frozen": return _("Frozen!");
294 case "tagged-target": return _("Tagged");
299 vector fixrgbexcess_move(vector rgb, vector src, vector dst)
301 vector yvec = '0.299 0.587 0.114';
302 return rgb + dst * ((src * yvec) / (dst * yvec)) * ((rgb - '1 1 1') * src);
304 vector fixrgbexcess(vector rgb)
308 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 1 1');
311 rgb = fixrgbexcess_move(rgb, '0 1 0', '0 0 1');
317 rgb = fixrgbexcess_move(rgb, '0 0 1', '0 1 0');
324 rgb = fixrgbexcess_move(rgb, '0 1 0', '1 0 1');
327 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 0 1');
333 rgb = fixrgbexcess_move(rgb, '0 0 1', '1 0 0');
340 rgb = fixrgbexcess_move(rgb, '0 0 1', '1 1 0');
343 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 1 0');
349 rgb = fixrgbexcess_move(rgb, '0 1 0', '1 0 0');
357 void Draw_WaypointSprite()
363 self.alpha = pow(bound(0, (self.fadetime - time) / self.lifetime, 1), waypointsprite_timealphaexponent);
367 if(self.hideflags & 2)
368 return; // radar only
370 if(autocvar_cl_hidewaypoints >= 2)
373 if(self.hideflags & 1)
374 if(autocvar_cl_hidewaypoints)
375 return; // fixed waypoint
377 InterpolateOrigin_Do();
379 t = GetPlayerColor(player_localentnum - 1) + 1;
386 case SPRITERULE_DEFAULT:
390 spriteimage = self.netname;
395 spriteimage = self.netname;
397 case SPRITERULE_TEAMPLAY:
398 if(t == COLOR_SPECTATOR + 1)
399 spriteimage = self.netname3;
400 else if(self.team == t)
401 spriteimage = self.netname2;
403 spriteimage = self.netname;
406 error("Invalid waypointsprite rule!");
410 if(spriteimage == "")
414 dist = vlen(self.origin - view_origin);
417 a = self.alpha * autocvar_hud_panel_fg_alpha;
419 if(self.maxdistance > waypointsprite_normdistance)
420 a *= pow(bound(0, (self.maxdistance - dist) / (self.maxdistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent);
421 else if(self.maxdistance > 0)
422 a *= pow(bound(0, (waypointsprite_fadedistance - dist) / (waypointsprite_fadedistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent) * (1 - waypointsprite_minalpha) + waypointsprite_minalpha;
425 rgb = self.teamradar_color;
426 rgb = spritelookupcolor(spriteimage, rgb);
428 if(time - floor(time) > 0.5)
429 a *= spritelookupblinkvalue(spriteimage);
440 rgb = fixrgbexcess(rgb);
445 o = project_3d_to_2d(self.origin);
446 if(o_z < 0 || o_x < 0 || o_y < 0 || o_x > vid_conwidth || o_y > vid_conheight)
448 // scale it to be just in view
452 d = o - '0.5 0 0' * vid_conwidth - '0 0.5 0' * vid_conheight;
453 ang = atan2(-d_x, -d_y);
457 f1 = d_x / vid_conwidth;
458 f2 = d_y / vid_conheight;
460 if(max(f1, -f1) > max(f2, -f2))
487 o = d + '0.5 0 0' * vid_conwidth + '0 0.5 0' * vid_conheight;
495 float edgedistance_min, crosshairdistance;
496 edgedistance_min = min4(o_y, o_x,vid_conwidth - o_x, vid_conheight - o_y);
499 vidscale = max(vid_conwidth / vid_width, vid_conheight / vid_height);
501 crosshairdistance = sqrt( pow(o_x - vid_conwidth/2, 2) + pow(o_y - vid_conheight/2, 2) );
503 t = waypointsprite_scale * vidscale;
504 a *= waypointsprite_alpha;
507 a = a * (1 - (1 - waypointsprite_distancefadealpha) * (bound(0, dist/waypointsprite_distancefadedistance, 1)));
508 t = t * (1 - (1 - waypointsprite_distancefadescale) * (bound(0, dist/waypointsprite_distancefadedistance, 1)));
510 if (edgedistance_min < waypointsprite_edgefadedistance) {
511 a = a * (1 - (1 - waypointsprite_edgefadealpha) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1)));
512 t = t * (1 - (1 - waypointsprite_edgefadescale) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1)));
514 if(crosshairdistance < waypointsprite_crosshairfadedistance) {
515 a = a * (1 - (1 - waypointsprite_crosshairfadealpha) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1)));
516 t = t * (1 - (1 - waypointsprite_crosshairfadescale) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1)));
519 if(self.build_finished)
521 if(time < self.build_finished + 0.25)
523 if(time < self.build_started)
524 self.health = self.build_starthealth;
525 else if(time < self.build_finished)
526 self.health = (time - self.build_started) / (self.build_finished - self.build_started) * (1 - self.build_starthealth) + self.build_starthealth;
534 o = drawspritearrow(o, ang, rgb, a, SPRITE_ARROW_SCALE * t);
537 txt = spritelookuptext(spriteimage);
538 if(autocvar_g_waypointsprite_uppercase)
539 txt = strtoupper(txt);
543 o = drawspritetext(o, ang, (SPRITE_HEALTHBAR_WIDTH + 2 * SPRITE_HEALTHBAR_BORDER) * t, rgb, a, waypointsprite_fontsize * '1 1 0', txt);
546 if(self.build_finished)
551 marg = -(SPRITE_HEALTHBAR_MARGIN + SPRITE_HEALTHBAR_HEIGHT + 2 * SPRITE_HEALTHBAR_BORDER) * t - 0.5 * waypointsprite_fontsize;
553 marg = SPRITE_HEALTHBAR_MARGIN * t + 0.5 * waypointsprite_fontsize;
560 SPRITE_HEALTHBAR_WIDTH * t,
561 SPRITE_HEALTHBAR_HEIGHT * t,
563 SPRITE_HEALTHBAR_BORDER * t,
566 a * SPRITE_HEALTHBAR_BORDERALPHA,
568 a * SPRITE_HEALTHBAR_HEALTHALPHA,
574 o = drawspritetext(o, ang, 0, rgb, a, waypointsprite_fontsize * '1 1 0', txt);
578 void Ent_RemoveWaypointSprite()
581 strunzone(self.netname);
583 strunzone(self.netname2);
585 strunzone(self.netname3);
588 void Ent_WaypointSprite()
590 float sendflags, f, t;
591 sendflags = ReadByte();
594 self.spawntime = time;
596 self.draw2d = Draw_WaypointSprite;
598 InterpolateOrigin_Undo();
605 self.health = t / 191.0;
606 self.build_finished = 0;
610 t = (t - 192) * 256 + ReadByte();
611 self.build_started = servertime;
612 if(self.build_finished)
613 self.build_starthealth = bound(0, self.health, 1);
615 self.build_starthealth = 0;
616 self.build_finished = servertime + t / 32;
622 self.build_finished = 0;
627 // unfortunately, this needs to be exact (for the 3D display)
628 self.origin_x = ReadCoord();
629 self.origin_y = ReadCoord();
630 self.origin_z = ReadCoord();
635 self.team = ReadByte();
636 self.rule = ReadByte();
642 strunzone(self.netname);
643 self.netname = strzone(ReadString());
649 strunzone(self.netname2);
650 self.netname2 = strzone(ReadString());
656 strunzone(self.netname3);
657 self.netname3 = strzone(ReadString());
662 self.lifetime = ReadCoord();
663 self.fadetime = ReadCoord();
664 self.maxdistance = ReadShort();
665 self.hideflags = ReadByte();
671 self.teamradar_icon = (f & 0x7F);
674 self.(teamradar_times[self.teamradar_time_index]) = time;
675 self.teamradar_time_index = mod(self.teamradar_time_index + 1, MAX_TEAMRADAR_TIMES);
677 self.teamradar_color_x = ReadByte() / 255.0;
678 self.teamradar_color_y = ReadByte() / 255.0;
679 self.teamradar_color_z = ReadByte() / 255.0;
682 InterpolateOrigin_Note();
684 self.entremove = Ent_RemoveWaypointSprite;
687 void WaypointSprite_Load_Frames(string ext)
689 float dh, n, i, o, f;
690 string s, sname, sframes;
691 dh = search_begin(strcat("models/sprites/*_frame*", ext), FALSE, FALSE);
694 float ext_len = strlen(ext);
695 n = search_getsize(dh);
696 for(i = 0; i < n; ++i)
698 s = search_getfilename(dh, i);
699 s = substring(s, 15, strlen(s) - 15 - ext_len); // strip models/sprites/ and extension
701 o = strstrofs(s, "_frame", 0);
702 sname = strcat("/spriteframes/", substring(s, 0, o));
703 sframes = substring(s, o + 6, strlen(s) - o - 6);
704 f = stof(sframes) + 1;
705 db_put(tempdb, sname, ftos(max(f, stof(db_get(tempdb, sname)))));
710 void WaypointSprite_Load()
712 waypointsprite_fadedistance = vlen(mi_scale);
713 waypointsprite_normdistance = autocvar_g_waypointsprite_normdistance;
714 waypointsprite_minscale = autocvar_g_waypointsprite_minscale;
715 waypointsprite_minalpha = autocvar_g_waypointsprite_minalpha;
716 waypointsprite_distancealphaexponent = autocvar_g_waypointsprite_distancealphaexponent;
717 waypointsprite_timealphaexponent = autocvar_g_waypointsprite_timealphaexponent;
718 waypointsprite_scale = autocvar_g_waypointsprite_scale;
719 waypointsprite_fontsize = autocvar_g_waypointsprite_fontsize;
720 waypointsprite_edgefadealpha = autocvar_g_waypointsprite_edgefadealpha;
721 waypointsprite_edgefadescale = autocvar_g_waypointsprite_edgefadescale;
722 waypointsprite_edgefadedistance = autocvar_g_waypointsprite_edgefadedistance;
723 waypointsprite_crosshairfadealpha = autocvar_g_waypointsprite_crosshairfadealpha;
724 waypointsprite_crosshairfadescale = autocvar_g_waypointsprite_crosshairfadescale;
725 waypointsprite_crosshairfadedistance = autocvar_g_waypointsprite_crosshairfadedistance;
726 waypointsprite_distancefadealpha = autocvar_g_waypointsprite_distancefadealpha;
727 waypointsprite_distancefadescale = autocvar_g_waypointsprite_distancefadescale;
728 waypointsprite_distancefadedistance = waypointsprite_fadedistance * autocvar_g_waypointsprite_distancefadedistancemultiplier;
729 waypointsprite_alpha = autocvar_g_waypointsprite_alpha * (1 - autocvar__menu_alpha);
731 if(!waypointsprite_initialized)
733 WaypointSprite_Load_Frames(".tga");
734 WaypointSprite_Load_Frames(".jpg");
735 waypointsprite_initialized = true;