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;
23 .string netname; // primary picture
24 .string netname2; // secondary picture
25 .string netname3; // tertiary picture
26 .float team; // team that gets netname2
34 .float build_starthealth;
35 .float build_finished;
37 float SPRITE_HEALTHBAR_WIDTH = 144;
38 float SPRITE_HEALTHBAR_HEIGHT = 9;
39 float SPRITE_HEALTHBAR_MARGIN = 6;
40 float SPRITE_HEALTHBAR_BORDER = 2;
41 float SPRITE_HEALTHBAR_BORDERALPHA = 1;
42 float SPRITE_HEALTHBAR_HEALTHALPHA = 0.5;
43 float SPRITE_ARROW_SCALE = 1.0;
44 float SPRITE_HELPME_BLINK = 2;
46 void drawrotpic(vector org, float rot, string pic, vector sz, vector hotspot, vector rgb, float a, float f)
48 vector v1, v2, v3, v4;
50 hotspot = -1 * hotspot;
52 // hotspot-relative coordinates of the corners
54 v2 = hotspot + '1 0 0' * sz_x;
55 v3 = hotspot + '1 0 0' * sz_x + '0 1 0' * sz_y;
56 v4 = hotspot + '0 1 0' * sz_y;
58 // rotate them, and make them absolute
59 rot = -rot; // rotate by the opposite angle, as our coordinate system is reversed
60 v1 = rotate(v1, rot) + org;
61 v2 = rotate(v2, rot) + org;
62 v3 = rotate(v3, rot) + org;
63 v4 = rotate(v4, rot) + org;
66 R_BeginPolygon(pic, f);
67 R_PolygonVertex(v1, '0 0 0', rgb, a);
68 R_PolygonVertex(v2, '1 0 0', rgb, a);
69 R_PolygonVertex(v3, '1 1 0', rgb, a);
70 R_PolygonVertex(v4, '0 1 0', rgb, a);
74 void drawquad(vector o, vector ri, vector up, string pic, vector rgb, float a, float f)
76 R_BeginPolygon(pic, f);
77 R_PolygonVertex(o, '0 0 0', rgb, a);
78 R_PolygonVertex(o + ri, '1 0 0', rgb, a);
79 R_PolygonVertex(o + up + ri, '1 1 0', rgb, a);
80 R_PolygonVertex(o + up, '0 1 0', rgb, a);
84 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)
87 float owidth; // outer width
89 hotspot = -1 * hotspot;
91 // hotspot-relative coordinates of the healthbar corners
96 rot = -rot; // rotate by the opposite angle, as our coordinate system is reversed
97 o = rotate(o, rot) + org;
101 owidth = width + 2 * border;
102 o = o - up * (margin + border + height) + ri * (sz_x - owidth) * 0.5;
104 drawquad(o - up * border, ri * owidth, up * border, "", rgb, a, f);
105 drawquad(o + up * height, ri * owidth, up * border, "", rgb, a, f);
106 drawquad(o, ri * border, up * height, "", rgb, a, f);
107 drawquad(o + ri * (owidth - border), ri * border, up * height, "", rgb, a, f);
108 drawquad(o + ri * (border + align * ((1 - h) * width)), ri * width * h, up * height, "", hrgb, ha, f);
111 // returns location of sprite text
112 vector drawspritearrow(vector o, float ang, vector rgb, float a, float t)
115 float BORDER; BORDER = 1.5 * t;
116 float TSIZE; TSIZE = 8 * t;
117 float RLENGTH; RLENGTH = 8 * t;
118 float RWIDTH; RWIDTH = 4 * t;
119 float MLENGTH; MLENGTH = 4 * t;
121 R_BeginPolygon("", DRAWFLAG_NORMAL);
122 R_PolygonVertex(o + rotate(eX * -(TSIZE + BORDER * (1 + SQRT2)) + eY * (TSIZE + BORDER), ang), '0 0 0', '0 0 0', a);
123 R_PolygonVertex(o + rotate(eX * (TSIZE + BORDER * (1 + SQRT2)) + eY * (TSIZE + BORDER), ang), '0 0 0', '0 0 0', a);
124 R_PolygonVertex(o + rotate(eY * -( BORDER * SQRT2), ang), '0 0 0', '0 0 0', a);
126 R_BeginPolygon("", DRAWFLAG_NORMAL);
127 R_PolygonVertex(o + rotate(eX * -(RWIDTH + BORDER) + eY * (TSIZE + BORDER), ang), '0 0 0', '0 0 0', a);
128 R_PolygonVertex(o + rotate(eX * -(RWIDTH + BORDER) + eY * (TSIZE + RLENGTH + BORDER), ang), '0 0 0', '0 0 0', a);
129 R_PolygonVertex(o + rotate(eX * (RWIDTH + BORDER) + eY * (TSIZE + RLENGTH + BORDER), ang), '0 0 0', '0 0 0', a);
130 R_PolygonVertex(o + rotate(eX * (RWIDTH + BORDER) + eY * (TSIZE + BORDER), ang), '0 0 0', '0 0 0', a);
133 R_BeginPolygon("", DRAWFLAG_ADDITIVE);
134 R_PolygonVertex(o + rotate(eX * -TSIZE + eY * TSIZE, ang), '0 0 0', rgb, a);
135 R_PolygonVertex(o + rotate(eX * TSIZE + eY * TSIZE, ang), '0 0 0', rgb, a);
136 R_PolygonVertex(o + rotate('0 0 0', ang), '0 0 0', rgb, a);
138 R_BeginPolygon("", DRAWFLAG_ADDITIVE);
139 R_PolygonVertex(o + rotate(eX * -RWIDTH + eY * TSIZE, ang), '0 0 0', rgb, a);
140 R_PolygonVertex(o + rotate(eX * -RWIDTH + eY * (TSIZE + RLENGTH), ang), '0 0 0', rgb, a);
141 R_PolygonVertex(o + rotate(eX * RWIDTH + eY * (TSIZE + RLENGTH), ang), '0 0 0', rgb, a);
142 R_PolygonVertex(o + rotate(eX * RWIDTH + eY * TSIZE, ang), '0 0 0', rgb, a);
146 o + rotate(eY * (TSIZE + RLENGTH + MLENGTH), ang);
149 // returns location of sprite healthbar
150 vector drawspritetext(vector o, float ang, float minwidth, vector rgb, float a, vector fontsize, string s)
154 float aspect, sa, ca;
156 sw = stringwidth(s, FALSE, fontsize);
163 // how do corners work?
164 aspect = vid_conwidth / vid_conheight;
166 ca = cos(ang) * aspect;
167 if(fabs(sa) > fabs(ca))
170 algny = 0.5 - 0.5 * ca / fabs(sa);
174 algnx = 0.5 - 0.5 * sa / fabs(ca);
182 // we want to be onscreen
187 if(o_x > vid_conwidth - w)
188 o_x = vid_conwidth - w;
189 if(o_y > vid_conheight - h)
190 o_x = vid_conheight - h;
192 o_x += 0.5 * (w - sw);
194 drawstring(o, s, fontsize, rgb, a, DRAWFLAG_NORMAL);
202 float spritelookupblinkvalue(string s)
206 case "ons-cp-atck-neut": return 2;
207 case "ons-cp-atck-red": return 2;
208 case "ons-cp-atck-blue": return 2;
209 case "ons-cp-dfnd-red": return 0.5;
210 case "ons-cp-dfnd-blue": return 0.5;
211 case "item-invis": return 2;
212 case "item-extralife": return 2;
213 case "item-speed": return 2;
214 case "item-strength": return 2;
215 case "item-shueld": return 2;
216 case "item-fuelregen": return 2;
217 case "item-jetpack": return 2;
218 case "tagged-target": return 2;
222 vector spritelookupcolor(string s, vector def)
226 case "keycarrier-friend": return '0 1 0';
230 string spritelookuptext(string s)
234 case "as-push": return _("Push");
235 case "as-destroy": return _("Destroy");
236 case "as-defend": return _("Defend");
237 case "bluebase": return _("Blue base");
238 case "danger": return _("DANGER");
239 case "flagcarrier": return _("Flag carrier");
240 case "flagdropped": return _("Dropped flag");
241 case "helpme": return _("Help me!");
242 case "here": return _("Here");
243 case "key-dropped": return _("Dropped key");
244 case "keycarrier-blue": return _("Key carrier");
245 case "keycarrier-finish": return _("Run here");
246 case "keycarrier-friend": return _("Key carrier");
247 case "keycarrier-pink": return _("Key carrier");
248 case "keycarrier-red": return _("Key carrier");
249 case "keycarrier-yellow": return _("Key carrier");
250 case "redbase": return _("Red base");
251 case "waypoint": return _("Waypoint");
252 case "ons-gen-red": return _("Generator");
253 case "ons-gen-blue": return _("Generator");
254 case "ons-gen-shielded": return _("Generator");
255 case "ons-cp-neut": return _("Control point");
256 case "ons-cp-red": return _("Control point");
257 case "ons-cp-blue": return _("Control point");
258 case "ons-cp-atck-neut": return _("Control point");
259 case "ons-cp-atck-red": return _("Control point");
260 case "ons-cp-atck-blue": return _("Control point");
261 case "ons-cp-dfnd-red": return _("Control point");
262 case "ons-cp-dfnd-blue": return _("Control point");
263 case "race-checkpoint": return _("Checkpoint");
264 case "race-finish": return _("Finish");
265 case "race-start": return _("Start");
266 case "nb-ball": return _("Ball");
267 case "ka-ball": return _("Ball");
268 case "ka-ballcarrier": return _("Ball carrier");
269 case "wpn-laser": return _("Laser");
270 case "wpn-shotgun": return _("Shotgun");
271 case "wpn-uzi": return _("Machine Gun");
272 case "wpn-gl": return _("Mortar");
273 case "wpn-electro": return _("Electro");
274 case "wpn-crylink": return _("Crylink");
275 case "wpn-nex": return _("Nex");
276 case "wpn-hagar": return _("Hagar");
277 case "wpn-rl": return _("Rocket Launcher");
278 case "wpn-porto": return _("Port-O-Launch");
279 case "wpn-minstanex": return _("Minstanex");
280 case "wpn-hookgun": return _("Hook");
281 case "wpn-fireball": return _("Fireball");
282 case "wpn-hlac": return _("HLAC");
283 case "wpn-campingrifle": return _("Rifle");
284 case "wpn-minelayer": return _("Mine Layer");
285 case "dom-neut": return _("Control point");
286 case "dom-red": return _("Control point");
287 case "dom-blue": return _("Control point");
288 case "dom-yellow": return _("Control point");
289 case "dom-pink": return _("Control point");
290 case "item-invis": return _("Invisibility");
291 case "item-extralife": return _("Extra life");
292 case "item-speed": return _("Speed");
293 case "item-strength": return _("Strength");
294 case "item-shield": return _("Shield");
295 case "item-fuelregen": return _("Fuel regen");
296 case "item-jetpack": return _("Jet Pack");
297 case "freezetag_frozen": return _("Frozen!");
298 case "tagged-target": return _("Tagged");
299 case "vehicle": return _("Vehicle");
304 vector fixrgbexcess_move(vector rgb, vector src, vector dst)
306 vector yvec = '0.299 0.587 0.114';
307 return rgb + dst * ((src * yvec) / (dst * yvec)) * ((rgb - '1 1 1') * src);
309 vector fixrgbexcess(vector rgb)
313 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 1 1');
316 rgb = fixrgbexcess_move(rgb, '0 1 0', '0 0 1');
322 rgb = fixrgbexcess_move(rgb, '0 0 1', '0 1 0');
329 rgb = fixrgbexcess_move(rgb, '0 1 0', '1 0 1');
332 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 0 1');
338 rgb = fixrgbexcess_move(rgb, '0 0 1', '1 0 0');
345 rgb = fixrgbexcess_move(rgb, '0 0 1', '1 1 0');
348 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 1 0');
354 rgb = fixrgbexcess_move(rgb, '0 1 0', '1 0 0');
362 void Draw_WaypointSprite()
368 self.alpha = pow(bound(0, (self.fadetime - time) / self.lifetime, 1), waypointsprite_timealphaexponent);
372 if(self.hideflags & 2)
373 return; // radar only
375 if(autocvar_cl_hidewaypoints >= 2)
378 if(self.hideflags & 1)
379 if(autocvar_cl_hidewaypoints)
380 return; // fixed waypoint
382 InterpolateOrigin_Do();
384 t = GetPlayerColor(player_localentnum - 1) + 1;
391 case SPRITERULE_DEFAULT:
395 spriteimage = self.netname;
400 spriteimage = self.netname;
402 case SPRITERULE_TEAMPLAY:
403 if(t == COLOR_SPECTATOR + 1)
404 spriteimage = self.netname3;
405 else if(self.team == t)
406 spriteimage = self.netname2;
408 spriteimage = self.netname;
411 error("Invalid waypointsprite rule!");
415 if(spriteimage == "")
419 dist = vlen(self.origin - view_origin);
422 a = self.alpha * autocvar_hud_panel_fg_alpha;
424 if(self.maxdistance > waypointsprite_normdistance)
425 a *= pow(bound(0, (self.maxdistance - dist) / (self.maxdistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent);
426 else if(self.maxdistance > 0)
427 a *= pow(bound(0, (waypointsprite_fadedistance - dist) / (waypointsprite_fadedistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent) * (1 - waypointsprite_minalpha) + waypointsprite_minalpha;
430 rgb = self.teamradar_color;
431 rgb = spritelookupcolor(spriteimage, rgb);
434 self.teamradar_color = '1 0 1';
435 print(sprintf("WARNING: sprite of name %s has no color, using pink so you notice it\n", spriteimage));
438 if(time - floor(time) > 0.5)
440 if(self.helpme && time < self.helpme)
441 a *= SPRITE_HELPME_BLINK;
443 a *= spritelookupblinkvalue(spriteimage);
455 rgb = fixrgbexcess(rgb);
460 o = project_3d_to_2d(self.origin);
461 if(o_z < 0 || o_x < 0 || o_y < 0 || o_x > vid_conwidth || o_y > vid_conheight)
463 // scale it to be just in view
467 d = o - '0.5 0 0' * vid_conwidth - '0 0.5 0' * vid_conheight;
468 ang = atan2(-d_x, -d_y);
472 f1 = d_x / vid_conwidth;
473 f2 = d_y / vid_conheight;
475 if(max(f1, -f1) > max(f2, -f2))
502 o = d + '0.5 0 0' * vid_conwidth + '0 0.5 0' * vid_conheight;
510 d = o - '0.5 0 0' * vid_conwidth - '0 0.5 0' * vid_conheight;
511 ang = atan2(-d_x, -d_y);
516 float edgedistance_min, crosshairdistance;
517 edgedistance_min = min4(o_y, o_x,vid_conwidth - o_x, vid_conheight - o_y);
520 vidscale = max(vid_conwidth / vid_width, vid_conheight / vid_height);
522 crosshairdistance = sqrt( pow(o_x - vid_conwidth/2, 2) + pow(o_y - vid_conheight/2, 2) );
524 t = waypointsprite_scale * vidscale;
525 a *= waypointsprite_alpha;
528 a = a * (1 - (1 - waypointsprite_distancefadealpha) * (bound(0, dist/waypointsprite_distancefadedistance, 1)));
529 t = t * (1 - (1 - waypointsprite_distancefadescale) * (bound(0, dist/waypointsprite_distancefadedistance, 1)));
531 if (edgedistance_min < waypointsprite_edgefadedistance) {
532 a = a * (1 - (1 - waypointsprite_edgefadealpha) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1)));
533 t = t * (1 - (1 - waypointsprite_edgefadescale) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1)));
535 if(crosshairdistance < waypointsprite_crosshairfadedistance) {
536 a = a * (1 - (1 - waypointsprite_crosshairfadealpha) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1)));
537 t = t * (1 - (1 - waypointsprite_crosshairfadescale) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1)));
540 if(self.build_finished)
542 if(time < self.build_finished + 0.25)
544 if(time < self.build_started)
545 self.health = self.build_starthealth;
546 else if(time < self.build_finished)
547 self.health = (time - self.build_started) / (self.build_finished - self.build_started) * (1 - self.build_starthealth) + self.build_starthealth;
555 o = drawspritearrow(o, ang, rgb, a, SPRITE_ARROW_SCALE * t);
558 txt = spritelookuptext(spriteimage);
559 if(self.helpme && time < self.helpme)
560 txt = sprintf(_("%s needing help!"), txt);
561 if(autocvar_g_waypointsprite_uppercase)
562 txt = strtoupper(txt);
566 o = drawspritetext(o, ang, (SPRITE_HEALTHBAR_WIDTH + 2 * SPRITE_HEALTHBAR_BORDER) * t, rgb, a, waypointsprite_fontsize * '1 1 0', txt);
569 if(self.build_finished)
574 marg = -(SPRITE_HEALTHBAR_MARGIN + SPRITE_HEALTHBAR_HEIGHT + 2 * SPRITE_HEALTHBAR_BORDER) * t - 0.5 * waypointsprite_fontsize;
576 marg = SPRITE_HEALTHBAR_MARGIN * t + 0.5 * waypointsprite_fontsize;
583 SPRITE_HEALTHBAR_WIDTH * t,
584 SPRITE_HEALTHBAR_HEIGHT * t,
586 SPRITE_HEALTHBAR_BORDER * t,
589 a * SPRITE_HEALTHBAR_BORDERALPHA,
591 a * SPRITE_HEALTHBAR_HEALTHALPHA,
597 o = drawspritetext(o, ang, 0, rgb, a, waypointsprite_fontsize * '1 1 0', txt);
601 void Ent_RemoveWaypointSprite()
604 strunzone(self.netname);
606 strunzone(self.netname2);
608 strunzone(self.netname3);
611 void Ent_WaypointSprite()
613 float sendflags, f, t;
614 sendflags = ReadByte();
617 self.spawntime = time;
619 self.draw2d = Draw_WaypointSprite;
621 InterpolateOrigin_Undo();
628 self.health = t / 191.0;
629 self.build_finished = 0;
633 t = (t - 192) * 256 + ReadByte();
634 self.build_started = servertime;
635 if(self.build_finished)
636 self.build_starthealth = bound(0, self.health, 1);
638 self.build_starthealth = 0;
639 self.build_finished = servertime + t / 32;
645 self.build_finished = 0;
650 // unfortunately, this needs to be exact (for the 3D display)
651 self.origin_x = ReadCoord();
652 self.origin_y = ReadCoord();
653 self.origin_z = ReadCoord();
658 self.team = ReadByte();
659 self.rule = ReadByte();
665 strunzone(self.netname);
666 self.netname = strzone(ReadString());
672 strunzone(self.netname2);
673 self.netname2 = strzone(ReadString());
679 strunzone(self.netname3);
680 self.netname3 = strzone(ReadString());
685 self.lifetime = ReadCoord();
686 self.fadetime = ReadCoord();
687 self.maxdistance = ReadShort();
688 self.hideflags = ReadByte();
694 self.teamradar_icon = (f & 0x7F);
697 self.(teamradar_times[self.teamradar_time_index]) = time;
698 self.teamradar_time_index = mod(self.teamradar_time_index + 1, MAX_TEAMRADAR_TIMES);
700 self.teamradar_color_x = ReadByte() / 255.0;
701 self.teamradar_color_y = ReadByte() / 255.0;
702 self.teamradar_color_z = ReadByte() / 255.0;
703 self.helpme = ReadByte() * 0.1;
705 self.helpme += servertime;
708 InterpolateOrigin_Note();
710 self.entremove = Ent_RemoveWaypointSprite;
713 void WaypointSprite_Load_Frames(string ext)
715 float dh, n, i, o, f;
716 string s, sname, sframes;
717 dh = search_begin(strcat("models/sprites/*_frame*", ext), FALSE, FALSE);
720 float ext_len = strlen(ext);
721 n = search_getsize(dh);
722 for(i = 0; i < n; ++i)
724 s = search_getfilename(dh, i);
725 s = substring(s, 15, strlen(s) - 15 - ext_len); // strip models/sprites/ and extension
727 o = strstrofs(s, "_frame", 0);
728 sname = strcat("/spriteframes/", substring(s, 0, o));
729 sframes = substring(s, o + 6, strlen(s) - o - 6);
730 f = stof(sframes) + 1;
731 db_put(tempdb, sname, ftos(max(f, stof(db_get(tempdb, sname)))));
736 void WaypointSprite_Load()
738 waypointsprite_fadedistance = vlen(mi_scale);
739 waypointsprite_normdistance = autocvar_g_waypointsprite_normdistance;
740 waypointsprite_minscale = autocvar_g_waypointsprite_minscale;
741 waypointsprite_minalpha = autocvar_g_waypointsprite_minalpha;
742 waypointsprite_distancealphaexponent = autocvar_g_waypointsprite_distancealphaexponent;
743 waypointsprite_timealphaexponent = autocvar_g_waypointsprite_timealphaexponent;
744 waypointsprite_scale = autocvar_g_waypointsprite_scale;
745 waypointsprite_fontsize = autocvar_g_waypointsprite_fontsize;
746 waypointsprite_edgefadealpha = autocvar_g_waypointsprite_edgefadealpha;
747 waypointsprite_edgefadescale = autocvar_g_waypointsprite_edgefadescale;
748 waypointsprite_edgefadedistance = autocvar_g_waypointsprite_edgefadedistance;
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;