2 #include "../dpdefs/csprogsdefs.qc"
4 #include "../common/constants.qh"
5 #include "../warpzonelib/mathlib.qh"
6 #include "../common/teams.qh"
7 #include "../common/util.qh"
8 #include "../common/buffs.qh"
9 #include "../common/weapons/weapons.qh"
10 #include "autocvars.qh"
11 #include "../csqcmodellib/interpolate.qh"
12 #include "teamradar.qh"
13 #include "waypointsprites.qh"
15 #include "../csqcmodellib/cl_model.qh"
16 #include "../server/t_items.qh"
21 float waypointsprite_initialized;
22 float waypointsprite_fadedistance;
23 float waypointsprite_normdistance;
24 float waypointsprite_minscale;
25 float waypointsprite_minalpha;
26 float waypointsprite_distancealphaexponent;
27 float waypointsprite_timealphaexponent;
28 float waypointsprite_scale;
29 float waypointsprite_fontsize;
30 float waypointsprite_edgefadealpha;
31 float waypointsprite_edgefadescale;
32 float waypointsprite_edgefadedistance;
33 float waypointsprite_edgeoffset_bottom;
34 float waypointsprite_edgeoffset_left;
35 float waypointsprite_edgeoffset_right;
36 float waypointsprite_edgeoffset_top;
37 float waypointsprite_crosshairfadealpha;
38 float waypointsprite_crosshairfadescale;
39 float waypointsprite_crosshairfadedistance;
40 float waypointsprite_distancefadealpha;
41 float waypointsprite_distancefadescale;
42 float waypointsprite_distancefadedistance;
43 float waypointsprite_alpha;
47 .string netname; // primary picture
48 .string netname2; // secondary picture
49 .string netname3; // tertiary picture
50 .float team; // team that gets netname2
58 .float build_starthealth;
59 .float build_finished;
61 const float SPRITE_HEALTHBAR_WIDTH = 144;
62 const float SPRITE_HEALTHBAR_HEIGHT = 9;
63 const float SPRITE_HEALTHBAR_MARGIN = 6;
64 const float SPRITE_HEALTHBAR_BORDER = 2;
65 const float SPRITE_HEALTHBAR_BORDERALPHA = 1;
66 const float SPRITE_HEALTHBAR_HEALTHALPHA = 0.5;
67 const float SPRITE_ARROW_SCALE = 1.0;
68 const float SPRITE_HELPME_BLINK = 2;
70 void drawrotpic(vector org, float rot, string pic, vector sz, vector hotspot, vector rgb, float a, float f)
72 vector v1, v2, v3, v4;
74 hotspot = -1 * hotspot;
76 // hotspot-relative coordinates of the corners
78 v2 = hotspot + '1 0 0' * sz.x;
79 v3 = hotspot + '1 0 0' * sz.x + '0 1 0' * sz.y;
80 v4 = hotspot + '0 1 0' * sz.y;
82 // rotate them, and make them absolute
83 rot = -rot; // rotate by the opposite angle, as our coordinate system is reversed
84 v1 = rotate(v1, rot) + org;
85 v2 = rotate(v2, rot) + org;
86 v3 = rotate(v3, rot) + org;
87 v4 = rotate(v4, rot) + org;
90 R_BeginPolygon(pic, f);
91 R_PolygonVertex(v1, '0 0 0', rgb, a);
92 R_PolygonVertex(v2, '1 0 0', rgb, a);
93 R_PolygonVertex(v3, '1 1 0', rgb, a);
94 R_PolygonVertex(v4, '0 1 0', rgb, a);
98 void drawquad(vector o, vector ri, vector up, string pic, vector rgb, float a, float f)
100 R_BeginPolygon(pic, f);
101 R_PolygonVertex(o, '0 0 0', rgb, a);
102 R_PolygonVertex(o + ri, '1 0 0', rgb, a);
103 R_PolygonVertex(o + up + ri, '1 1 0', rgb, a);
104 R_PolygonVertex(o + up, '0 1 0', rgb, a);
108 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)
111 float owidth; // outer width
113 hotspot = -1 * hotspot;
115 // hotspot-relative coordinates of the healthbar corners
120 rot = -rot; // rotate by the opposite angle, as our coordinate system is reversed
121 o = rotate(o, rot) + org;
122 ri = rotate(ri, rot);
123 up = rotate(up, rot);
125 owidth = width + 2 * border;
126 o = o - up * (margin + border + height) + ri * (sz.x - owidth) * 0.5;
128 drawquad(o - up * border, ri * owidth, up * border, "", rgb, a, f);
129 drawquad(o + up * height, ri * owidth, up * border, "", rgb, a, f);
130 drawquad(o, ri * border, up * height, "", rgb, a, f);
131 drawquad(o + ri * (owidth - border), ri * border, up * height, "", rgb, a, f);
132 drawquad(o + ri * (border + align * ((1 - h) * width)), ri * width * h, up * height, "", hrgb, ha, f);
135 // returns location of sprite text
136 vector drawspritearrow(vector o, float ang, vector rgb, float a, float t)
138 float size = 9.0 * t;
139 float border = 1.5 * t;
140 float margin = 4.0 * t;
142 float borderDiag = border * 1.414;
143 vector arrowX = eX * size;
144 vector arrowY = eY * (size+borderDiag);
145 vector borderX = eX * (size+borderDiag);
146 vector borderY = eY * (size+borderDiag+border);
148 R_BeginPolygon("", DRAWFLAG_NORMAL);
149 R_PolygonVertex(o, '0 0 0', '0 0 0', a);
150 R_PolygonVertex(o + rotate(arrowY - borderX, ang), '0 0 0', '0 0 0', a);
151 R_PolygonVertex(o + rotate(borderY - borderX, ang), '0 0 0', '0 0 0', a);
152 R_PolygonVertex(o + rotate(borderY + borderX, ang), '0 0 0', '0 0 0', a);
153 R_PolygonVertex(o + rotate(arrowY + borderX, ang), '0 0 0', '0 0 0', a);
156 R_BeginPolygon("", DRAWFLAG_ADDITIVE);
157 R_PolygonVertex(o + rotate(eY * borderDiag, ang), '0 0 0', rgb, a);
158 R_PolygonVertex(o + rotate(arrowY - arrowX, ang), '0 0 0', rgb, a);
159 R_PolygonVertex(o + rotate(arrowY + arrowX, ang), '0 0 0', rgb, a);
162 return o + rotate(eY * (borderDiag+size+margin), ang);
165 // returns location of sprite healthbar
166 vector drawspritetext(vector o, float ang, float minwidth, vector rgb, float a, vector fontsize, string s)
170 float aspect, sa, ca;
172 sw = stringwidth(s, false, fontsize);
179 // how do corners work?
180 aspect = vid_conwidth / vid_conheight;
182 ca = cos(ang) * aspect;
183 if(fabs(sa) > fabs(ca))
186 algny = 0.5 - 0.5 * ca / fabs(sa);
190 algnx = 0.5 - 0.5 * sa / fabs(ca);
198 // we want to be onscreen
203 if(o.x > vid_conwidth - w)
204 o_x = vid_conwidth - w;
205 if(o.y > vid_conheight - h)
206 o_x = vid_conheight - h;
208 o.x += 0.5 * (w - sw);
210 drawstring(o, s, fontsize, rgb, a, DRAWFLAG_NORMAL);
218 float spritelookupblinkvalue(string s)
222 case "ons-cp-atck-neut": return 2;
223 case "ons-cp-atck-red": return 2;
224 case "ons-cp-atck-blue": return 2;
225 case "ons-cp-dfnd-red": return 0.5;
226 case "ons-cp-dfnd-blue": return 0.5;
227 case "item-invis": return 2;
228 case "item-extralife": return 2;
229 case "item-speed": return 2;
230 case "item-strength": return 2;
231 case "item-shield": return 2;
232 case "item-fuelregen": return 2;
233 case "item-jetpack": return 2;
234 case "tagged-target": return 2;
238 vector spritelookupcolor(string s, vector def)
240 if(substring(s, 0, 4) == "wpn-")
241 return (get_weaponinfo(stof(substring(s, 4, strlen(s)))).wpcolor);
245 case "keycarrier-friend": return '0 1 0';
249 string spritelookuptext(string s)
251 if(substring(s, 0, 4) == "wpn-") { return (get_weaponinfo(stof(substring(s, 4, strlen(s)))).message); }
252 if(substring(s, 0, 5) == "buff-") { return Buff_PrettyName(Buff_Type_FromSprite(s)); }
256 case "as-push": return _("Push");
257 case "as-destroy": return _("Destroy");
258 case "as-defend": return _("Defend");
259 case "bluebase": return _("Blue base");
260 case "danger": return _("DANGER");
261 case "enemyflagcarrier": return _("Enemy carrier");
262 case "flagcarrier": return _("Flag carrier");
263 case "flagdropped": return _("Dropped flag");
264 case "helpme": return _("Help me!");
265 case "here": return _("Here");
266 case "key-dropped": return _("Dropped key");
267 case "keycarrier-blue": return _("Key carrier");
268 case "keycarrier-finish": return _("Run here");
269 case "keycarrier-friend": return _("Key carrier");
270 case "keycarrier-pink": return _("Key carrier");
271 case "keycarrier-red": return _("Key carrier");
272 case "keycarrier-yellow": return _("Key carrier");
273 case "redbase": return _("Red base");
274 case "waypoint": return _("Waypoint");
275 case "ons-gen-red": return _("Generator");
276 case "ons-gen-blue": return _("Generator");
277 case "ons-gen-shielded": return _("Generator");
278 case "ons-cp-neut": return _("Control point");
279 case "ons-cp-red": return _("Control point");
280 case "ons-cp-blue": return _("Control point");
281 case "ons-cp-atck-neut": return _("Control point");
282 case "ons-cp-atck-red": return _("Control point");
283 case "ons-cp-atck-blue": return _("Control point");
284 case "ons-cp-dfnd-red": return _("Control point");
285 case "ons-cp-dfnd-blue": return _("Control point");
286 case "race-checkpoint": return _("Checkpoint");
287 case "race-finish": return _("Finish");
288 case "race-start": return _("Start");
289 case "race-start-finish": return (race_checkpointtime || race_mycheckpointtime) ? _("Finish") : _("Start");
290 case "goal": return _("Goal");
291 case "nb-ball": return _("Ball");
292 case "ka-ball": return _("Ball");
293 case "ka-ballcarrier": return _("Ball carrier");
294 case "dom-neut": return _("Control point");
295 case "dom-red": return _("Control point");
296 case "dom-blue": return _("Control point");
297 case "dom-yellow": return _("Control point");
298 case "dom-pink": return _("Control point");
299 case "item-invis": return _("Invisibility");
300 case "item-extralife": return _("Extra life");
301 case "item-speed": return _("Speed");
302 case "item-strength": return _("Strength");
303 case "item-shield": return _("Shield");
304 case "item-fuelregen": return _("Fuel regen");
305 case "item-jetpack": return _("Jet Pack");
306 case "frozen": return _("Frozen!");
307 case "tagged-target": return _("Tagged");
308 case "vehicle": return _("Vehicle");
313 vector fixrgbexcess_move(vector rgb, vector src, vector dst)
315 vector yvec = '0.299 0.587 0.114';
316 return rgb + dst * ((src * yvec) / (dst * yvec)) * ((rgb - '1 1 1') * src);
318 vector fixrgbexcess(vector rgb)
322 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 1 1');
325 rgb = fixrgbexcess_move(rgb, '0 1 0', '0 0 1');
331 rgb = fixrgbexcess_move(rgb, '0 0 1', '0 1 0');
338 rgb = fixrgbexcess_move(rgb, '0 1 0', '1 0 1');
341 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 0 1');
347 rgb = fixrgbexcess_move(rgb, '0 0 1', '1 0 0');
354 rgb = fixrgbexcess_move(rgb, '0 0 1', '1 1 0');
357 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 1 0');
363 rgb = fixrgbexcess_move(rgb, '0 1 0', '1 0 0');
371 float waypointsprite_count, waypointsprite_newcount;
372 void Draw_WaypointSprite()
378 self.alpha = pow(bound(0, (self.fadetime - time) / self.lifetime, 1), waypointsprite_timealphaexponent);
382 if(self.hideflags & 2)
383 return; // radar only
385 if(autocvar_cl_hidewaypoints >= 2)
388 if(self.hideflags & 1)
389 if(autocvar_cl_hidewaypoints)
390 return; // fixed waypoint
392 InterpolateOrigin_Do();
394 t = GetPlayerColor(player_localnum) + 1;
401 case SPRITERULE_DEFAULT:
405 spriteimage = self.netname;
410 spriteimage = self.netname;
412 case SPRITERULE_TEAMPLAY:
413 if(t == NUM_SPECTATOR + 1)
414 spriteimage = self.netname3;
415 else if(self.team == t)
416 spriteimage = self.netname2;
418 spriteimage = self.netname;
421 error("Invalid waypointsprite rule!");
425 if(spriteimage == "")
428 ++waypointsprite_newcount;
431 dist = vlen(self.origin - view_origin);
434 a = self.alpha * autocvar_hud_panel_fg_alpha;
436 if(self.maxdistance > waypointsprite_normdistance)
437 a *= pow(bound(0, (self.maxdistance - dist) / (self.maxdistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent);
438 else if(self.maxdistance > 0)
439 a *= pow(bound(0, (waypointsprite_fadedistance - dist) / (waypointsprite_fadedistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent) * (1 - waypointsprite_minalpha) + waypointsprite_minalpha;
442 rgb = self.teamradar_color;
443 rgb = spritelookupcolor(spriteimage, rgb);
446 self.teamradar_color = '1 0 1';
447 printf("WARNING: sprite of name %s has no color, using pink so you notice it\n", spriteimage);
450 if(time - floor(time) > 0.5)
452 if(self.helpme && time < self.helpme)
453 a *= SPRITE_HELPME_BLINK;
455 a *= spritelookupblinkvalue(spriteimage);
467 rgb = fixrgbexcess(rgb);
472 o = project_3d_to_2d(self.origin);
474 || o.x < (vid_conwidth * waypointsprite_edgeoffset_left)
475 || o.y < (vid_conheight * waypointsprite_edgeoffset_top)
476 || o.x > (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right))
477 || o.y > (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom)))
479 // scale it to be just in view
483 d = o - '0.5 0 0' * vid_conwidth - '0 0.5 0' * vid_conheight;
484 ang = atan2(-d.x, -d.y);
488 f1 = d.x / vid_conwidth;
489 f2 = d.y / vid_conheight;
491 if(max(f1, -f1) > max(f2, -f2))
496 d = d * ((0.5 - waypointsprite_edgeoffset_right) / f1);
501 d = d * (-(0.5 - waypointsprite_edgeoffset_left) / f1);
509 d = d * ((0.5 - waypointsprite_edgeoffset_bottom) / f2);
514 d = d * (-(0.5 - waypointsprite_edgeoffset_top) / f2);
518 o = d + '0.5 0 0' * vid_conwidth + '0 0.5 0' * vid_conheight;
526 d = o - '0.5 0 0' * vid_conwidth - '0 0.5 0' * vid_conheight;
527 ang = atan2(-d.x, -d.y);
532 float edgedistance_min, crosshairdistance;
533 edgedistance_min = min((o.y - (vid_conheight * waypointsprite_edgeoffset_top)),
534 (o.x - (vid_conwidth * waypointsprite_edgeoffset_left)),
535 (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right)) - o.x,
536 (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom)) - o.y);
539 vidscale = max(vid_conwidth / vid_width, vid_conheight / vid_height);
541 crosshairdistance = sqrt( pow(o.x - vid_conwidth/2, 2) + pow(o.y - vid_conheight/2, 2) );
543 t = waypointsprite_scale * vidscale;
544 a *= waypointsprite_alpha;
547 a = a * (1 - (1 - waypointsprite_distancefadealpha) * (bound(0, dist/waypointsprite_distancefadedistance, 1)));
548 t = t * (1 - (1 - waypointsprite_distancefadescale) * (bound(0, dist/waypointsprite_distancefadedistance, 1)));
550 if (edgedistance_min < waypointsprite_edgefadedistance) {
551 a = a * (1 - (1 - waypointsprite_edgefadealpha) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1)));
552 t = t * (1 - (1 - waypointsprite_edgefadescale) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1)));
554 if(crosshairdistance < waypointsprite_crosshairfadedistance) {
555 a = a * (1 - (1 - waypointsprite_crosshairfadealpha) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1)));
556 t = t * (1 - (1 - waypointsprite_crosshairfadescale) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1)));
559 if(self.build_finished)
561 if(time < self.build_finished + 0.25)
563 if(time < self.build_started)
564 self.health = self.build_starthealth;
565 else if(time < self.build_finished)
566 self.health = (time - self.build_started) / (self.build_finished - self.build_started) * (1 - self.build_starthealth) + self.build_starthealth;
574 o = drawspritearrow(o, ang, rgb, a, SPRITE_ARROW_SCALE * t);
577 if(autocvar_g_waypointsprite_spam && waypointsprite_count >= autocvar_g_waypointsprite_spam)
580 txt = spritelookuptext(spriteimage);
581 if(self.helpme && time < self.helpme)
582 txt = sprintf(_("%s needing help!"), txt);
583 if(autocvar_g_waypointsprite_uppercase)
584 txt = strtoupper(txt);
586 draw_beginBoldFont();
589 o = drawspritetext(o, ang, (SPRITE_HEALTHBAR_WIDTH + 2 * SPRITE_HEALTHBAR_BORDER) * t, rgb, a, waypointsprite_fontsize * '1 1 0', txt);
592 if(self.build_finished)
597 marg = -(SPRITE_HEALTHBAR_MARGIN + SPRITE_HEALTHBAR_HEIGHT + 2 * SPRITE_HEALTHBAR_BORDER) * t - 0.5 * waypointsprite_fontsize;
599 marg = SPRITE_HEALTHBAR_MARGIN * t + 0.5 * waypointsprite_fontsize;
606 SPRITE_HEALTHBAR_WIDTH * t,
607 SPRITE_HEALTHBAR_HEIGHT * t,
609 SPRITE_HEALTHBAR_BORDER * t,
612 a * SPRITE_HEALTHBAR_BORDERALPHA,
614 a * SPRITE_HEALTHBAR_HEALTHALPHA,
620 o = drawspritetext(o, ang, 0, rgb, a, waypointsprite_fontsize * '1 1 0', txt);
625 void Ent_RemoveWaypointSprite()
628 strunzone(self.netname);
630 strunzone(self.netname2);
632 strunzone(self.netname3);
635 void Ent_WaypointSprite()
638 sendflags = ReadByte();
641 self.spawntime = time;
643 self.draw2d = Draw_WaypointSprite;
645 InterpolateOrigin_Undo();
646 self.iflags |= IFLAG_ORIGIN;
653 self.health = t / 191.0;
654 self.build_finished = 0;
658 t = (t - 192) * 256 + ReadByte();
659 self.build_started = servertime;
660 if(self.build_finished)
661 self.build_starthealth = bound(0, self.health, 1);
663 self.build_starthealth = 0;
664 self.build_finished = servertime + t / 32;
670 self.build_finished = 0;
675 // unfortunately, this needs to be exact (for the 3D display)
676 self.origin_x = ReadCoord();
677 self.origin_y = ReadCoord();
678 self.origin_z = ReadCoord();
679 setorigin(self, self.origin);
684 self.team = ReadByte();
685 self.rule = ReadByte();
691 strunzone(self.netname);
692 self.netname = strzone(ReadString());
698 strunzone(self.netname2);
699 self.netname2 = strzone(ReadString());
705 strunzone(self.netname3);
706 self.netname3 = strzone(ReadString());
711 self.lifetime = ReadCoord();
712 self.fadetime = ReadCoord();
713 self.maxdistance = ReadShort();
714 self.hideflags = ReadByte();
720 self.teamradar_icon = (f & 0x7F);
723 self.(teamradar_times[self.teamradar_time_index]) = time;
724 self.teamradar_time_index = (self.teamradar_time_index + 1) % MAX_TEAMRADAR_TIMES;
726 self.teamradar_color_x = ReadByte() / 255.0;
727 self.teamradar_color_y = ReadByte() / 255.0;
728 self.teamradar_color_z = ReadByte() / 255.0;
729 self.helpme = ReadByte() * 0.1;
731 self.helpme += servertime;
734 InterpolateOrigin_Note();
736 self.entremove = Ent_RemoveWaypointSprite;
739 void WaypointSprite_Load_Frames(string ext)
741 float dh, n, i, o, f;
742 string s, sname, sframes;
743 dh = search_begin(strcat("models/sprites/*_frame*", ext), false, false);
746 float ext_len = strlen(ext);
747 n = search_getsize(dh);
748 for(i = 0; i < n; ++i)
750 s = search_getfilename(dh, i);
751 s = substring(s, 15, strlen(s) - 15 - ext_len); // strip models/sprites/ and extension
753 o = strstrofs(s, "_frame", 0);
754 sname = strcat("/spriteframes/", substring(s, 0, o));
755 sframes = substring(s, o + 6, strlen(s) - o - 6);
756 f = stof(sframes) + 1;
757 db_put(tempdb, sname, ftos(max(f, stof(db_get(tempdb, sname)))));
762 void WaypointSprite_Load()
764 waypointsprite_fadedistance = vlen(mi_scale);
765 waypointsprite_normdistance = autocvar_g_waypointsprite_normdistance;
766 waypointsprite_minscale = autocvar_g_waypointsprite_minscale;
767 waypointsprite_minalpha = autocvar_g_waypointsprite_minalpha;
768 waypointsprite_distancealphaexponent = autocvar_g_waypointsprite_distancealphaexponent;
769 waypointsprite_timealphaexponent = autocvar_g_waypointsprite_timealphaexponent;
770 waypointsprite_scale = autocvar_g_waypointsprite_scale;
771 waypointsprite_fontsize = autocvar_g_waypointsprite_fontsize;
772 waypointsprite_edgefadealpha = autocvar_g_waypointsprite_edgefadealpha;
773 waypointsprite_edgefadescale = autocvar_g_waypointsprite_edgefadescale;
774 waypointsprite_edgefadedistance = autocvar_g_waypointsprite_edgefadedistance;
775 waypointsprite_edgeoffset_bottom = autocvar_g_waypointsprite_edgeoffset_bottom;
776 waypointsprite_edgeoffset_left = autocvar_g_waypointsprite_edgeoffset_left;
777 waypointsprite_edgeoffset_right = autocvar_g_waypointsprite_edgeoffset_right;
778 waypointsprite_edgeoffset_top = autocvar_g_waypointsprite_edgeoffset_top;
779 waypointsprite_crosshairfadealpha = autocvar_g_waypointsprite_crosshairfadealpha;
780 waypointsprite_crosshairfadescale = autocvar_g_waypointsprite_crosshairfadescale;
781 waypointsprite_crosshairfadedistance = autocvar_g_waypointsprite_crosshairfadedistance;
782 waypointsprite_distancefadealpha = autocvar_g_waypointsprite_distancefadealpha;
783 waypointsprite_distancefadescale = autocvar_g_waypointsprite_distancefadescale;
784 waypointsprite_distancefadedistance = waypointsprite_fadedistance * autocvar_g_waypointsprite_distancefadedistancemultiplier;
785 waypointsprite_alpha = autocvar_g_waypointsprite_alpha * (1 - autocvar__menu_alpha);
787 if(!waypointsprite_initialized)
789 WaypointSprite_Load_Frames(".tga");
790 WaypointSprite_Load_Frames(".jpg");
791 waypointsprite_initialized = true;
794 waypointsprite_count = waypointsprite_newcount;
795 waypointsprite_newcount = 0;