1 #include "waypointsprites.qh"
3 #include "autocvars.qh"
7 #include "miscfunctions.qh"
8 #include "teamradar.qh"
9 #include "../common/buffs.qh"
10 #include "../common/constants.qh"
11 #include "../common/teams.qh"
12 #include "../common/weapons/weapons.qh"
13 #include "../csqcmodellib/interpolate.qh"
14 #include "../warpzonelib/mathlib.qh"
18 void drawrotpic(vector org, float rot, string pic, vector sz, vector hotspot, vector rgb, float a, float f)
20 vector v1, v2, v3, v4;
22 hotspot = -1 * hotspot;
24 // hotspot-relative coordinates of the corners
26 v2 = hotspot + '1 0 0' * sz.x;
27 v3 = hotspot + '1 0 0' * sz.x + '0 1 0' * sz.y;
28 v4 = hotspot + '0 1 0' * sz.y;
30 // rotate them, and make them absolute
31 rot = -rot; // rotate by the opposite angle, as our coordinate system is reversed
32 v1 = rotate(v1, rot) + org;
33 v2 = rotate(v2, rot) + org;
34 v3 = rotate(v3, rot) + org;
35 v4 = rotate(v4, rot) + org;
38 R_BeginPolygon(pic, f);
39 R_PolygonVertex(v1, '0 0 0', rgb, a);
40 R_PolygonVertex(v2, '1 0 0', rgb, a);
41 R_PolygonVertex(v3, '1 1 0', rgb, a);
42 R_PolygonVertex(v4, '0 1 0', rgb, a);
46 void drawquad(vector o, vector ri, vector up, string pic, vector rgb, float a, float f)
48 R_BeginPolygon(pic, f);
49 R_PolygonVertex(o, '0 0 0', rgb, a);
50 R_PolygonVertex(o + ri, '1 0 0', rgb, a);
51 R_PolygonVertex(o + up + ri, '1 1 0', rgb, a);
52 R_PolygonVertex(o + up, '0 1 0', rgb, a);
56 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)
59 float owidth; // outer width
61 hotspot = -1 * hotspot;
63 // hotspot-relative coordinates of the healthbar corners
68 rot = -rot; // rotate by the opposite angle, as our coordinate system is reversed
69 o = rotate(o, rot) + org;
73 owidth = width + 2 * border;
74 o = o - up * (margin + border + height) + ri * (sz.x - owidth) * 0.5;
76 drawquad(o - up * border, ri * owidth, up * border, "", rgb, a, f);
77 drawquad(o + up * height, ri * owidth, up * border, "", rgb, a, f);
78 drawquad(o, ri * border, up * height, "", rgb, a, f);
79 drawquad(o + ri * (owidth - border), ri * border, up * height, "", rgb, a, f);
80 drawquad(o + ri * (border + align * ((1 - h) * width)), ri * width * h, up * height, "", hrgb, ha, f);
83 // returns location of sprite text
84 vector drawspritearrow(vector o, float ang, vector rgb, float a, float t)
87 float border = 1.5 * t;
88 float margin = 4.0 * t;
90 float borderDiag = border * 1.414;
91 vector arrowX = eX * size;
92 vector arrowY = eY * (size+borderDiag);
93 vector borderX = eX * (size+borderDiag);
94 vector borderY = eY * (size+borderDiag+border);
96 R_BeginPolygon("", DRAWFLAG_NORMAL);
97 R_PolygonVertex(o, '0 0 0', '0 0 0', a);
98 R_PolygonVertex(o + rotate(arrowY - borderX, ang), '0 0 0', '0 0 0', a);
99 R_PolygonVertex(o + rotate(borderY - borderX, ang), '0 0 0', '0 0 0', a);
100 R_PolygonVertex(o + rotate(borderY + borderX, ang), '0 0 0', '0 0 0', a);
101 R_PolygonVertex(o + rotate(arrowY + borderX, ang), '0 0 0', '0 0 0', a);
104 R_BeginPolygon("", DRAWFLAG_ADDITIVE);
105 R_PolygonVertex(o + rotate(eY * borderDiag, ang), '0 0 0', rgb, a);
106 R_PolygonVertex(o + rotate(arrowY - arrowX, ang), '0 0 0', rgb, a);
107 R_PolygonVertex(o + rotate(arrowY + arrowX, ang), '0 0 0', rgb, a);
110 return o + rotate(eY * (borderDiag+size+margin), ang);
113 // returns location of sprite healthbar
114 vector drawspritetext(vector o, float ang, float minwidth, vector rgb, float a, vector fontsize, string s)
118 float aspect, sa, ca;
120 sw = stringwidth(s, false, fontsize);
127 // how do corners work?
128 aspect = vid_conwidth / vid_conheight;
130 ca = cos(ang) * aspect;
131 if(fabs(sa) > fabs(ca))
134 algny = 0.5 - 0.5 * ca / fabs(sa);
138 algnx = 0.5 - 0.5 * sa / fabs(ca);
146 // we want to be onscreen
151 if(o.x > vid_conwidth - w)
152 o.x = vid_conwidth - w;
153 if(o.y > vid_conheight - h)
154 o.x = vid_conheight - h;
156 o.x += 0.5 * (w - sw);
158 drawstring(o, s, fontsize, rgb, a, DRAWFLAG_NORMAL);
166 float spritelookupblinkvalue(string s)
170 case "ons-cp-atck-neut": return 2;
171 case "ons-cp-atck-red": return 2;
172 case "ons-cp-atck-blue": return 2;
173 case "ons-cp-dfnd-red": return 0.5;
174 case "ons-cp-dfnd-blue": return 0.5;
175 case "item-invis": return 2;
176 case "item-extralife": return 2;
177 case "item-speed": return 2;
178 case "item-strength": return 2;
179 case "item-shield": return 2;
180 case "item-fuelregen": return 2;
181 case "item-jetpack": return 2;
182 case "tagged-target": return 2;
186 vector spritelookupcolor(string s, vector def)
188 if(substring(s, 0, 4) == "wpn-")
189 return (get_weaponinfo(stof(substring(s, 4, strlen(s)))).wpcolor);
193 case "keycarrier-friend": return '0 1 0';
197 string spritelookuptext(string s)
199 if(substring(s, 0, 4) == "wpn-") { return (get_weaponinfo(stof(substring(s, 4, strlen(s)))).message); }
200 if(substring(s, 0, 5) == "buff-") { return Buff_PrettyName(Buff_Type_FromSprite(s)); }
204 case "as-push": return _("Push");
205 case "as-destroy": return _("Destroy");
206 case "as-defend": return _("Defend");
207 case "bluebase": return _("Blue base");
208 case "danger": return _("DANGER");
209 case "enemyflagcarrier": return _("Enemy carrier");
210 case "flagcarrier": return _("Flag carrier");
211 case "flagdropped": return _("Dropped flag");
212 case "helpme": return _("Help me!");
213 case "here": return _("Here");
214 case "key-dropped": return _("Dropped key");
215 case "keycarrier-blue": return _("Key carrier");
216 case "keycarrier-finish": return _("Run here");
217 case "keycarrier-friend": return _("Key carrier");
218 case "keycarrier-pink": return _("Key carrier");
219 case "keycarrier-red": return _("Key carrier");
220 case "keycarrier-yellow": return _("Key carrier");
221 case "redbase": return _("Red base");
222 case "waypoint": return _("Waypoint");
223 case "ons-gen-red": return _("Generator");
224 case "ons-gen-blue": return _("Generator");
225 case "ons-gen-shielded": return _("Generator");
226 case "ons-cp-neut": return _("Control point");
227 case "ons-cp-red": return _("Control point");
228 case "ons-cp-blue": return _("Control point");
229 case "ons-cp-atck-neut": return _("Control point");
230 case "ons-cp-atck-red": return _("Control point");
231 case "ons-cp-atck-blue": return _("Control point");
232 case "ons-cp-dfnd-red": return _("Control point");
233 case "ons-cp-dfnd-blue": return _("Control point");
234 case "race-checkpoint": return _("Checkpoint");
235 case "race-finish": return _("Finish");
236 case "race-start": return _("Start");
237 case "race-start-finish": return (race_checkpointtime || race_mycheckpointtime) ? _("Finish") : _("Start");
238 case "goal": return _("Goal");
239 case "nb-ball": return _("Ball");
240 case "ka-ball": return _("Ball");
241 case "ka-ballcarrier": return _("Ball carrier");
242 case "dom-neut": return _("Control point");
243 case "dom-red": return _("Control point");
244 case "dom-blue": return _("Control point");
245 case "dom-yellow": return _("Control point");
246 case "dom-pink": return _("Control point");
247 case "item-invis": return _("Invisibility");
248 case "item-extralife": return _("Extra life");
249 case "item-speed": return _("Speed");
250 case "item-strength": return _("Strength");
251 case "item-shield": return _("Shield");
252 case "item-fuelregen": return _("Fuel regen");
253 case "item-jetpack": return _("Jet Pack");
254 case "frozen": return _("Frozen!");
255 case "tagged-target": return _("Tagged");
256 case "vehicle": return _("Vehicle");
261 vector fixrgbexcess_move(vector rgb, vector src, vector dst)
263 vector yvec = '0.299 0.587 0.114';
264 return rgb + dst * ((src * yvec) / (dst * yvec)) * ((rgb - '1 1 1') * src);
266 vector fixrgbexcess(vector rgb)
270 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 1 1');
273 rgb = fixrgbexcess_move(rgb, '0 1 0', '0 0 1');
279 rgb = fixrgbexcess_move(rgb, '0 0 1', '0 1 0');
286 rgb = fixrgbexcess_move(rgb, '0 1 0', '1 0 1');
289 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 0 1');
295 rgb = fixrgbexcess_move(rgb, '0 0 1', '1 0 0');
302 rgb = fixrgbexcess_move(rgb, '0 0 1', '1 1 0');
305 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 1 0');
311 rgb = fixrgbexcess_move(rgb, '0 1 0', '1 0 0');
319 void Draw_WaypointSprite()
325 self.alpha = pow(bound(0, (self.fadetime - time) / self.lifetime, 1), waypointsprite_timealphaexponent);
329 if(self.hideflags & 2)
330 return; // radar only
332 if(autocvar_cl_hidewaypoints >= 2)
335 if(self.hideflags & 1)
336 if(autocvar_cl_hidewaypoints)
337 return; // fixed waypoint
339 InterpolateOrigin_Do();
341 t = GetPlayerColor(player_localnum) + 1;
348 case SPRITERULE_DEFAULT:
352 spriteimage = self.netname;
357 spriteimage = self.netname;
359 case SPRITERULE_TEAMPLAY:
360 if(t == NUM_SPECTATOR + 1)
361 spriteimage = self.netname3;
362 else if(self.team == t)
363 spriteimage = self.netname2;
365 spriteimage = self.netname;
368 error("Invalid waypointsprite rule!");
372 if(spriteimage == "")
375 ++waypointsprite_newcount;
378 dist = vlen(self.origin - view_origin);
381 a = self.alpha * autocvar_hud_panel_fg_alpha;
383 if(self.maxdistance > waypointsprite_normdistance)
384 a *= pow(bound(0, (self.maxdistance - dist) / (self.maxdistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent);
385 else if(self.maxdistance > 0)
386 a *= pow(bound(0, (waypointsprite_fadedistance - dist) / (waypointsprite_fadedistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent) * (1 - waypointsprite_minalpha) + waypointsprite_minalpha;
389 rgb = self.teamradar_color;
390 rgb = spritelookupcolor(spriteimage, rgb);
393 self.teamradar_color = '1 0 1';
394 printf("WARNING: sprite of name %s has no color, using pink so you notice it\n", spriteimage);
397 if(time - floor(time) > 0.5)
399 if(self.helpme && time < self.helpme)
400 a *= SPRITE_HELPME_BLINK;
402 a *= spritelookupblinkvalue(spriteimage);
414 rgb = fixrgbexcess(rgb);
419 o = project_3d_to_2d(self.origin);
421 || o.x < (vid_conwidth * waypointsprite_edgeoffset_left)
422 || o.y < (vid_conheight * waypointsprite_edgeoffset_top)
423 || o.x > (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right))
424 || o.y > (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom)))
426 // scale it to be just in view
430 d = o - '0.5 0 0' * vid_conwidth - '0 0.5 0' * vid_conheight;
431 ang = atan2(-d.x, -d.y);
435 f1 = d.x / vid_conwidth;
436 f2 = d.y / vid_conheight;
438 if(max(f1, -f1) > max(f2, -f2))
443 d = d * ((0.5 - waypointsprite_edgeoffset_right) / f1);
448 d = d * (-(0.5 - waypointsprite_edgeoffset_left) / f1);
456 d = d * ((0.5 - waypointsprite_edgeoffset_bottom) / f2);
461 d = d * (-(0.5 - waypointsprite_edgeoffset_top) / f2);
465 o = d + '0.5 0 0' * vid_conwidth + '0 0.5 0' * vid_conheight;
473 d = o - '0.5 0 0' * vid_conwidth - '0 0.5 0' * vid_conheight;
474 ang = atan2(-d.x, -d.y);
479 float edgedistance_min, crosshairdistance;
480 edgedistance_min = min((o.y - (vid_conheight * waypointsprite_edgeoffset_top)),
481 (o.x - (vid_conwidth * waypointsprite_edgeoffset_left)),
482 (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right)) - o.x,
483 (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom)) - o.y);
486 vidscale = max(vid_conwidth / vid_width, vid_conheight / vid_height);
488 crosshairdistance = sqrt( pow(o.x - vid_conwidth/2, 2) + pow(o.y - vid_conheight/2, 2) );
490 t = waypointsprite_scale * vidscale;
491 a *= waypointsprite_alpha;
494 a = a * (1 - (1 - waypointsprite_distancefadealpha) * (bound(0, dist/waypointsprite_distancefadedistance, 1)));
495 t = t * (1 - (1 - waypointsprite_distancefadescale) * (bound(0, dist/waypointsprite_distancefadedistance, 1)));
497 if (edgedistance_min < waypointsprite_edgefadedistance) {
498 a = a * (1 - (1 - waypointsprite_edgefadealpha) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1)));
499 t = t * (1 - (1 - waypointsprite_edgefadescale) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1)));
501 if(crosshairdistance < waypointsprite_crosshairfadedistance) {
502 a = a * (1 - (1 - waypointsprite_crosshairfadealpha) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1)));
503 t = t * (1 - (1 - waypointsprite_crosshairfadescale) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1)));
506 if(self.build_finished)
508 if(time < self.build_finished + 0.25)
510 if(time < self.build_started)
511 self.health = self.build_starthealth;
512 else if(time < self.build_finished)
513 self.health = (time - self.build_started) / (self.build_finished - self.build_started) * (1 - self.build_starthealth) + self.build_starthealth;
521 o = drawspritearrow(o, ang, rgb, a, SPRITE_ARROW_SCALE * t);
524 if(autocvar_g_waypointsprite_spam && waypointsprite_count >= autocvar_g_waypointsprite_spam)
527 txt = spritelookuptext(spriteimage);
528 if(self.helpme && time < self.helpme)
529 txt = sprintf(_("%s needing help!"), txt);
530 if(autocvar_g_waypointsprite_uppercase)
531 txt = strtoupper(txt);
533 draw_beginBoldFont();
536 o = drawspritetext(o, ang, (SPRITE_HEALTHBAR_WIDTH + 2 * SPRITE_HEALTHBAR_BORDER) * t, rgb, a, waypointsprite_fontsize * '1 1 0', txt);
539 if(self.build_finished)
544 marg = -(SPRITE_HEALTHBAR_MARGIN + SPRITE_HEALTHBAR_HEIGHT + 2 * SPRITE_HEALTHBAR_BORDER) * t - 0.5 * waypointsprite_fontsize;
546 marg = SPRITE_HEALTHBAR_MARGIN * t + 0.5 * waypointsprite_fontsize;
553 SPRITE_HEALTHBAR_WIDTH * t,
554 SPRITE_HEALTHBAR_HEIGHT * t,
556 SPRITE_HEALTHBAR_BORDER * t,
559 a * SPRITE_HEALTHBAR_BORDERALPHA,
561 a * SPRITE_HEALTHBAR_HEALTHALPHA,
567 o = drawspritetext(o, ang, 0, rgb, a, waypointsprite_fontsize * '1 1 0', txt);
572 void Ent_RemoveWaypointSprite()
575 strunzone(self.netname);
577 strunzone(self.netname2);
579 strunzone(self.netname3);
582 void Ent_WaypointSprite()
585 sendflags = ReadByte();
588 self.spawntime = time;
590 self.draw2d = Draw_WaypointSprite;
592 InterpolateOrigin_Undo();
593 self.iflags |= IFLAG_ORIGIN;
600 self.health = t / 191.0;
601 self.build_finished = 0;
605 t = (t - 192) * 256 + ReadByte();
606 self.build_started = servertime;
607 if(self.build_finished)
608 self.build_starthealth = bound(0, self.health, 1);
610 self.build_starthealth = 0;
611 self.build_finished = servertime + t / 32;
617 self.build_finished = 0;
622 // unfortunately, this needs to be exact (for the 3D display)
623 self.origin_x = ReadCoord();
624 self.origin_y = ReadCoord();
625 self.origin_z = ReadCoord();
626 setorigin(self, self.origin);
631 self.team = ReadByte();
632 self.rule = ReadByte();
638 strunzone(self.netname);
639 self.netname = strzone(ReadString());
645 strunzone(self.netname2);
646 self.netname2 = strzone(ReadString());
652 strunzone(self.netname3);
653 self.netname3 = strzone(ReadString());
658 self.lifetime = ReadCoord();
659 self.fadetime = ReadCoord();
660 self.maxdistance = ReadShort();
661 self.hideflags = ReadByte();
667 self.teamradar_icon = (f & 0x7F);
670 self.(teamradar_times[self.teamradar_time_index]) = time;
671 self.teamradar_time_index = (self.teamradar_time_index + 1) % MAX_TEAMRADAR_TIMES;
673 self.teamradar_color_x = ReadByte() / 255.0;
674 self.teamradar_color_y = ReadByte() / 255.0;
675 self.teamradar_color_z = ReadByte() / 255.0;
676 self.helpme = ReadByte() * 0.1;
678 self.helpme += servertime;
681 InterpolateOrigin_Note();
683 self.entremove = Ent_RemoveWaypointSprite;
686 void WaypointSprite_Load_Frames(string ext)
688 float dh, n, i, o, f;
689 string s, sname, sframes;
690 dh = search_begin(strcat("models/sprites/*_frame*", ext), false, false);
693 float ext_len = strlen(ext);
694 n = search_getsize(dh);
695 for(i = 0; i < n; ++i)
697 s = search_getfilename(dh, i);
698 s = substring(s, 15, strlen(s) - 15 - ext_len); // strip models/sprites/ and extension
700 o = strstrofs(s, "_frame", 0);
701 sname = strcat("/spriteframes/", substring(s, 0, o));
702 sframes = substring(s, o + 6, strlen(s) - o - 6);
703 f = stof(sframes) + 1;
704 db_put(tempdb, sname, ftos(max(f, stof(db_get(tempdb, sname)))));
709 void WaypointSprite_Load()
711 waypointsprite_fadedistance = vlen(mi_scale);
712 waypointsprite_normdistance = autocvar_g_waypointsprite_normdistance;
713 waypointsprite_minscale = autocvar_g_waypointsprite_minscale;
714 waypointsprite_minalpha = autocvar_g_waypointsprite_minalpha;
715 waypointsprite_distancealphaexponent = autocvar_g_waypointsprite_distancealphaexponent;
716 waypointsprite_timealphaexponent = autocvar_g_waypointsprite_timealphaexponent;
717 waypointsprite_scale = autocvar_g_waypointsprite_scale;
718 waypointsprite_fontsize = autocvar_g_waypointsprite_fontsize;
719 waypointsprite_edgefadealpha = autocvar_g_waypointsprite_edgefadealpha;
720 waypointsprite_edgefadescale = autocvar_g_waypointsprite_edgefadescale;
721 waypointsprite_edgefadedistance = autocvar_g_waypointsprite_edgefadedistance;
722 waypointsprite_edgeoffset_bottom = autocvar_g_waypointsprite_edgeoffset_bottom;
723 waypointsprite_edgeoffset_left = autocvar_g_waypointsprite_edgeoffset_left;
724 waypointsprite_edgeoffset_right = autocvar_g_waypointsprite_edgeoffset_right;
725 waypointsprite_edgeoffset_top = autocvar_g_waypointsprite_edgeoffset_top;
726 waypointsprite_crosshairfadealpha = autocvar_g_waypointsprite_crosshairfadealpha;
727 waypointsprite_crosshairfadescale = autocvar_g_waypointsprite_crosshairfadescale;
728 waypointsprite_crosshairfadedistance = autocvar_g_waypointsprite_crosshairfadedistance;
729 waypointsprite_distancefadealpha = autocvar_g_waypointsprite_distancefadealpha;
730 waypointsprite_distancefadescale = autocvar_g_waypointsprite_distancefadescale;
731 waypointsprite_distancefadedistance = waypointsprite_fadedistance * autocvar_g_waypointsprite_distancefadedistancemultiplier;
732 waypointsprite_alpha = autocvar_g_waypointsprite_alpha * (1 - autocvar__menu_alpha);
734 if(!waypointsprite_initialized)
736 WaypointSprite_Load_Frames(".tga");
737 WaypointSprite_Load_Frames(".jpg");
738 waypointsprite_initialized = true;
741 waypointsprite_count = waypointsprite_newcount;
742 waypointsprite_newcount = 0;