1 #include "waypointsprites.qh"
3 #include "autocvars.qh"
7 #include "miscfunctions.qh"
8 #include "teamradar.qh"
10 #include "../common/buffs.qh"
11 #include "../common/constants.qh"
12 #include "../common/teams.qh"
14 #include "../common/weapons/weapons.qh"
16 #include "../csqcmodellib/interpolate.qh"
18 #include "../warpzonelib/mathlib.qh"
22 void drawrotpic(vector org, float rot, string pic, vector sz, vector hotspot, vector rgb, float a, float f)
24 vector v1, v2, v3, v4;
26 hotspot = -1 * hotspot;
28 // hotspot-relative coordinates of the corners
30 v2 = hotspot + '1 0 0' * sz.x;
31 v3 = hotspot + '1 0 0' * sz.x + '0 1 0' * sz.y;
32 v4 = hotspot + '0 1 0' * sz.y;
34 // rotate them, and make them absolute
35 rot = -rot; // rotate by the opposite angle, as our coordinate system is reversed
36 v1 = rotate(v1, rot) + org;
37 v2 = rotate(v2, rot) + org;
38 v3 = rotate(v3, rot) + org;
39 v4 = rotate(v4, rot) + org;
42 R_BeginPolygon(pic, f);
43 R_PolygonVertex(v1, '0 0 0', rgb, a);
44 R_PolygonVertex(v2, '1 0 0', rgb, a);
45 R_PolygonVertex(v3, '1 1 0', rgb, a);
46 R_PolygonVertex(v4, '0 1 0', rgb, a);
50 void drawquad(vector o, vector ri, vector up, string pic, vector rgb, float a, float f)
52 R_BeginPolygon(pic, f);
53 R_PolygonVertex(o, '0 0 0', rgb, a);
54 R_PolygonVertex(o + ri, '1 0 0', rgb, a);
55 R_PolygonVertex(o + up + ri, '1 1 0', rgb, a);
56 R_PolygonVertex(o + up, '0 1 0', rgb, a);
60 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)
63 float owidth; // outer width
65 hotspot = -1 * hotspot;
67 // hotspot-relative coordinates of the healthbar corners
72 rot = -rot; // rotate by the opposite angle, as our coordinate system is reversed
73 o = rotate(o, rot) + org;
77 owidth = width + 2 * border;
78 o = o - up * (margin + border + height) + ri * (sz.x - owidth) * 0.5;
80 drawquad(o - up * border, ri * owidth, up * border, "", rgb, a, f);
81 drawquad(o + up * height, ri * owidth, up * border, "", rgb, a, f);
82 drawquad(o, ri * border, up * height, "", rgb, a, f);
83 drawquad(o + ri * (owidth - border), ri * border, up * height, "", rgb, a, f);
84 drawquad(o + ri * (border + align * ((1 - h) * width)), ri * width * h, up * height, "", hrgb, ha, f);
87 // returns location of sprite text
88 vector drawspritearrow(vector o, float ang, vector rgb, float a, float t)
91 float border = 1.5 * t;
92 float margin = 4.0 * t;
94 float borderDiag = border * 1.414;
95 vector arrowX = eX * size;
96 vector arrowY = eY * (size+borderDiag);
97 vector borderX = eX * (size+borderDiag);
98 vector borderY = eY * (size+borderDiag+border);
100 R_BeginPolygon("", DRAWFLAG_NORMAL);
101 R_PolygonVertex(o, '0 0 0', '0 0 0', a);
102 R_PolygonVertex(o + rotate(arrowY - borderX, ang), '0 0 0', '0 0 0', a);
103 R_PolygonVertex(o + rotate(borderY - borderX, ang), '0 0 0', '0 0 0', a);
104 R_PolygonVertex(o + rotate(borderY + borderX, ang), '0 0 0', '0 0 0', a);
105 R_PolygonVertex(o + rotate(arrowY + borderX, ang), '0 0 0', '0 0 0', a);
108 R_BeginPolygon("", DRAWFLAG_ADDITIVE);
109 R_PolygonVertex(o + rotate(eY * borderDiag, ang), '0 0 0', rgb, a);
110 R_PolygonVertex(o + rotate(arrowY - arrowX, ang), '0 0 0', rgb, a);
111 R_PolygonVertex(o + rotate(arrowY + arrowX, ang), '0 0 0', rgb, a);
114 return o + rotate(eY * (borderDiag+size+margin), ang);
117 // returns location of sprite healthbar
118 vector drawspritetext(vector o, float ang, float minwidth, vector rgb, float a, vector fontsize, string s)
122 float aspect, sa, ca;
124 sw = stringwidth(s, false, fontsize);
131 // how do corners work?
132 aspect = vid_conwidth / vid_conheight;
134 ca = cos(ang) * aspect;
135 if(fabs(sa) > fabs(ca))
138 algny = 0.5 - 0.5 * ca / fabs(sa);
142 algnx = 0.5 - 0.5 * sa / fabs(ca);
150 // we want to be onscreen
155 if(o.x > vid_conwidth - w)
156 o.x = vid_conwidth - w;
157 if(o.y > vid_conheight - h)
158 o.x = vid_conheight - h;
160 o.x += 0.5 * (w - sw);
162 drawstring(o, s, fontsize, rgb, a, DRAWFLAG_NORMAL);
170 float spritelookupblinkvalue(string s)
174 case "ons-cp-atck-neut": return 2;
175 case "ons-cp-atck-red": return 2;
176 case "ons-cp-atck-blue": return 2;
177 case "ons-cp-dfnd-red": return 0.5;
178 case "ons-cp-dfnd-blue": return 0.5;
179 case "item-invis": return 2;
180 case "item-extralife": return 2;
181 case "item-speed": return 2;
182 case "item-strength": return 2;
183 case "item-shield": return 2;
184 case "item-fuelregen": return 2;
185 case "item-jetpack": return 2;
186 case "tagged-target": return 2;
190 vector spritelookupcolor(string s, vector def)
192 if(substring(s, 0, 4) == "wpn-")
193 return (get_weaponinfo(stof(substring(s, 4, strlen(s)))).wpcolor);
197 case "keycarrier-friend": return '0 1 0';
201 string spritelookuptext(string s)
203 if(substring(s, 0, 4) == "wpn-") { return (get_weaponinfo(stof(substring(s, 4, strlen(s)))).message); }
204 if(substring(s, 0, 5) == "buff-") { return Buff_PrettyName(Buff_Type_FromSprite(s)); }
208 case "as-push": return _("Push");
209 case "as-destroy": return _("Destroy");
210 case "as-defend": return _("Defend");
211 case "bluebase": return _("Blue base");
212 case "danger": return _("DANGER");
213 case "enemyflagcarrier": return _("Enemy carrier");
214 case "flagcarrier": return _("Flag carrier");
215 case "flagdropped": return _("Dropped flag");
216 case "helpme": return _("Help me!");
217 case "here": return _("Here");
218 case "key-dropped": return _("Dropped key");
219 case "keycarrier-blue": return _("Key carrier");
220 case "keycarrier-finish": return _("Run here");
221 case "keycarrier-friend": return _("Key carrier");
222 case "keycarrier-pink": return _("Key carrier");
223 case "keycarrier-red": return _("Key carrier");
224 case "keycarrier-yellow": return _("Key carrier");
225 case "redbase": return _("Red base");
226 case "waypoint": return _("Waypoint");
227 case "ons-gen-red": return _("Generator");
228 case "ons-gen-blue": return _("Generator");
229 case "ons-gen-shielded": return _("Generator");
230 case "ons-cp-neut": return _("Control point");
231 case "ons-cp-red": return _("Control point");
232 case "ons-cp-blue": return _("Control point");
233 case "ons-cp-atck-neut": return _("Control point");
234 case "ons-cp-atck-red": return _("Control point");
235 case "ons-cp-atck-blue": return _("Control point");
236 case "ons-cp-dfnd-red": return _("Control point");
237 case "ons-cp-dfnd-blue": return _("Control point");
238 case "race-checkpoint": return _("Checkpoint");
239 case "race-finish": return _("Finish");
240 case "race-start": return _("Start");
241 case "race-start-finish": return (race_checkpointtime || race_mycheckpointtime) ? _("Finish") : _("Start");
242 case "goal": return _("Goal");
243 case "nb-ball": return _("Ball");
244 case "ka-ball": return _("Ball");
245 case "ka-ballcarrier": return _("Ball carrier");
246 case "dom-neut": return _("Control point");
247 case "dom-red": return _("Control point");
248 case "dom-blue": return _("Control point");
249 case "dom-yellow": return _("Control point");
250 case "dom-pink": return _("Control point");
251 case "item-invis": return _("Invisibility");
252 case "item-extralife": return _("Extra life");
253 case "item-speed": return _("Speed");
254 case "item-strength": return _("Strength");
255 case "item-shield": return _("Shield");
256 case "item-fuelregen": return _("Fuel regen");
257 case "item-jetpack": return _("Jet Pack");
258 case "frozen": return _("Frozen!");
259 case "tagged-target": return _("Tagged");
260 case "vehicle": return _("Vehicle");
265 vector fixrgbexcess_move(vector rgb, vector src, vector dst)
267 vector yvec = '0.299 0.587 0.114';
268 return rgb + dst * ((src * yvec) / (dst * yvec)) * ((rgb - '1 1 1') * src);
270 vector fixrgbexcess(vector rgb)
274 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 1 1');
277 rgb = fixrgbexcess_move(rgb, '0 1 0', '0 0 1');
283 rgb = fixrgbexcess_move(rgb, '0 0 1', '0 1 0');
290 rgb = fixrgbexcess_move(rgb, '0 1 0', '1 0 1');
293 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 0 1');
299 rgb = fixrgbexcess_move(rgb, '0 0 1', '1 0 0');
306 rgb = fixrgbexcess_move(rgb, '0 0 1', '1 1 0');
309 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 1 0');
315 rgb = fixrgbexcess_move(rgb, '0 1 0', '1 0 0');
323 void Draw_WaypointSprite()
329 self.alpha = pow(bound(0, (self.fadetime - time) / self.lifetime, 1), waypointsprite_timealphaexponent);
333 if(self.hideflags & 2)
334 return; // radar only
336 if(autocvar_cl_hidewaypoints >= 2)
339 if(self.hideflags & 1)
340 if(autocvar_cl_hidewaypoints)
341 return; // fixed waypoint
343 InterpolateOrigin_Do();
345 t = GetPlayerColor(player_localnum) + 1;
352 case SPRITERULE_DEFAULT:
356 spriteimage = self.netname;
361 spriteimage = self.netname;
363 case SPRITERULE_TEAMPLAY:
364 if(t == NUM_SPECTATOR + 1)
365 spriteimage = self.netname3;
366 else if(self.team == t)
367 spriteimage = self.netname2;
369 spriteimage = self.netname;
372 error("Invalid waypointsprite rule!");
376 if(spriteimage == "")
379 ++waypointsprite_newcount;
382 dist = vlen(self.origin - view_origin);
385 a = self.alpha * autocvar_hud_panel_fg_alpha;
387 if(self.maxdistance > waypointsprite_normdistance)
388 a *= pow(bound(0, (self.maxdistance - dist) / (self.maxdistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent);
389 else if(self.maxdistance > 0)
390 a *= pow(bound(0, (waypointsprite_fadedistance - dist) / (waypointsprite_fadedistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent) * (1 - waypointsprite_minalpha) + waypointsprite_minalpha;
393 rgb = self.teamradar_color;
394 rgb = spritelookupcolor(spriteimage, rgb);
397 self.teamradar_color = '1 0 1';
398 printf("WARNING: sprite of name %s has no color, using pink so you notice it\n", spriteimage);
401 if(time - floor(time) > 0.5)
403 if(self.helpme && time < self.helpme)
404 a *= SPRITE_HELPME_BLINK;
406 a *= spritelookupblinkvalue(spriteimage);
418 rgb = fixrgbexcess(rgb);
423 o = project_3d_to_2d(self.origin);
425 || o.x < (vid_conwidth * waypointsprite_edgeoffset_left)
426 || o.y < (vid_conheight * waypointsprite_edgeoffset_top)
427 || o.x > (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right))
428 || o.y > (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom)))
430 // scale it to be just in view
434 d = o - '0.5 0 0' * vid_conwidth - '0 0.5 0' * vid_conheight;
435 ang = atan2(-d.x, -d.y);
439 f1 = d.x / vid_conwidth;
440 f2 = d.y / vid_conheight;
442 if(max(f1, -f1) > max(f2, -f2))
447 d = d * ((0.5 - waypointsprite_edgeoffset_right) / f1);
452 d = d * (-(0.5 - waypointsprite_edgeoffset_left) / f1);
460 d = d * ((0.5 - waypointsprite_edgeoffset_bottom) / f2);
465 d = d * (-(0.5 - waypointsprite_edgeoffset_top) / f2);
469 o = d + '0.5 0 0' * vid_conwidth + '0 0.5 0' * vid_conheight;
477 d = o - '0.5 0 0' * vid_conwidth - '0 0.5 0' * vid_conheight;
478 ang = atan2(-d.x, -d.y);
483 float edgedistance_min, crosshairdistance;
484 edgedistance_min = min((o.y - (vid_conheight * waypointsprite_edgeoffset_top)),
485 (o.x - (vid_conwidth * waypointsprite_edgeoffset_left)),
486 (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right)) - o.x,
487 (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom)) - o.y);
490 vidscale = max(vid_conwidth / vid_width, vid_conheight / vid_height);
492 crosshairdistance = sqrt( pow(o.x - vid_conwidth/2, 2) + pow(o.y - vid_conheight/2, 2) );
494 t = waypointsprite_scale * vidscale;
495 a *= waypointsprite_alpha;
498 a = a * (1 - (1 - waypointsprite_distancefadealpha) * (bound(0, dist/waypointsprite_distancefadedistance, 1)));
499 t = t * (1 - (1 - waypointsprite_distancefadescale) * (bound(0, dist/waypointsprite_distancefadedistance, 1)));
501 if (edgedistance_min < waypointsprite_edgefadedistance) {
502 a = a * (1 - (1 - waypointsprite_edgefadealpha) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1)));
503 t = t * (1 - (1 - waypointsprite_edgefadescale) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1)));
505 if(crosshairdistance < waypointsprite_crosshairfadedistance) {
506 a = a * (1 - (1 - waypointsprite_crosshairfadealpha) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1)));
507 t = t * (1 - (1 - waypointsprite_crosshairfadescale) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1)));
510 if(self.build_finished)
512 if(time < self.build_finished + 0.25)
514 if(time < self.build_started)
515 self.health = self.build_starthealth;
516 else if(time < self.build_finished)
517 self.health = (time - self.build_started) / (self.build_finished - self.build_started) * (1 - self.build_starthealth) + self.build_starthealth;
525 o = drawspritearrow(o, ang, rgb, a, SPRITE_ARROW_SCALE * t);
528 if(autocvar_g_waypointsprite_spam && waypointsprite_count >= autocvar_g_waypointsprite_spam)
531 txt = spritelookuptext(spriteimage);
532 if(self.helpme && time < self.helpme)
533 txt = sprintf(_("%s needing help!"), txt);
534 if(autocvar_g_waypointsprite_uppercase)
535 txt = strtoupper(txt);
537 draw_beginBoldFont();
540 o = drawspritetext(o, ang, (SPRITE_HEALTHBAR_WIDTH + 2 * SPRITE_HEALTHBAR_BORDER) * t, rgb, a, waypointsprite_fontsize * '1 1 0', txt);
543 if(self.build_finished)
548 marg = -(SPRITE_HEALTHBAR_MARGIN + SPRITE_HEALTHBAR_HEIGHT + 2 * SPRITE_HEALTHBAR_BORDER) * t - 0.5 * waypointsprite_fontsize;
550 marg = SPRITE_HEALTHBAR_MARGIN * t + 0.5 * waypointsprite_fontsize;
557 SPRITE_HEALTHBAR_WIDTH * t,
558 SPRITE_HEALTHBAR_HEIGHT * t,
560 SPRITE_HEALTHBAR_BORDER * t,
563 a * SPRITE_HEALTHBAR_BORDERALPHA,
565 a * SPRITE_HEALTHBAR_HEALTHALPHA,
571 o = drawspritetext(o, ang, 0, rgb, a, waypointsprite_fontsize * '1 1 0', txt);
576 void Ent_RemoveWaypointSprite()
579 strunzone(self.netname);
581 strunzone(self.netname2);
583 strunzone(self.netname3);
586 void Ent_WaypointSprite()
589 sendflags = ReadByte();
592 self.spawntime = time;
594 self.draw2d = Draw_WaypointSprite;
596 InterpolateOrigin_Undo();
597 self.iflags |= IFLAG_ORIGIN;
604 self.health = t / 191.0;
605 self.build_finished = 0;
609 t = (t - 192) * 256 + ReadByte();
610 self.build_started = servertime;
611 if(self.build_finished)
612 self.build_starthealth = bound(0, self.health, 1);
614 self.build_starthealth = 0;
615 self.build_finished = servertime + t / 32;
621 self.build_finished = 0;
626 // unfortunately, this needs to be exact (for the 3D display)
627 self.origin_x = ReadCoord();
628 self.origin_y = ReadCoord();
629 self.origin_z = ReadCoord();
630 setorigin(self, self.origin);
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 = (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;
680 self.helpme = ReadByte() * 0.1;
682 self.helpme += servertime;
685 InterpolateOrigin_Note();
687 self.entremove = Ent_RemoveWaypointSprite;
690 void WaypointSprite_Load_Frames(string ext)
692 float dh, n, i, o, f;
693 string s, sname, sframes;
694 dh = search_begin(strcat("models/sprites/*_frame*", ext), false, false);
697 float ext_len = strlen(ext);
698 n = search_getsize(dh);
699 for(i = 0; i < n; ++i)
701 s = search_getfilename(dh, i);
702 s = substring(s, 15, strlen(s) - 15 - ext_len); // strip models/sprites/ and extension
704 o = strstrofs(s, "_frame", 0);
705 sname = strcat("/spriteframes/", substring(s, 0, o));
706 sframes = substring(s, o + 6, strlen(s) - o - 6);
707 f = stof(sframes) + 1;
708 db_put(tempdb, sname, ftos(max(f, stof(db_get(tempdb, sname)))));
713 void WaypointSprite_Load()
715 waypointsprite_fadedistance = vlen(mi_scale);
716 waypointsprite_normdistance = autocvar_g_waypointsprite_normdistance;
717 waypointsprite_minscale = autocvar_g_waypointsprite_minscale;
718 waypointsprite_minalpha = autocvar_g_waypointsprite_minalpha;
719 waypointsprite_distancealphaexponent = autocvar_g_waypointsprite_distancealphaexponent;
720 waypointsprite_timealphaexponent = autocvar_g_waypointsprite_timealphaexponent;
721 waypointsprite_scale = autocvar_g_waypointsprite_scale;
722 waypointsprite_fontsize = autocvar_g_waypointsprite_fontsize;
723 waypointsprite_edgefadealpha = autocvar_g_waypointsprite_edgefadealpha;
724 waypointsprite_edgefadescale = autocvar_g_waypointsprite_edgefadescale;
725 waypointsprite_edgefadedistance = autocvar_g_waypointsprite_edgefadedistance;
726 waypointsprite_edgeoffset_bottom = autocvar_g_waypointsprite_edgeoffset_bottom;
727 waypointsprite_edgeoffset_left = autocvar_g_waypointsprite_edgeoffset_left;
728 waypointsprite_edgeoffset_right = autocvar_g_waypointsprite_edgeoffset_right;
729 waypointsprite_edgeoffset_top = autocvar_g_waypointsprite_edgeoffset_top;
730 waypointsprite_crosshairfadealpha = autocvar_g_waypointsprite_crosshairfadealpha;
731 waypointsprite_crosshairfadescale = autocvar_g_waypointsprite_crosshairfadescale;
732 waypointsprite_crosshairfadedistance = autocvar_g_waypointsprite_crosshairfadedistance;
733 waypointsprite_distancefadealpha = autocvar_g_waypointsprite_distancefadealpha;
734 waypointsprite_distancefadescale = autocvar_g_waypointsprite_distancefadescale;
735 waypointsprite_distancefadedistance = waypointsprite_fadedistance * autocvar_g_waypointsprite_distancefadedistancemultiplier;
736 waypointsprite_alpha = autocvar_g_waypointsprite_alpha * (1 - autocvar__menu_alpha);
738 if(!waypointsprite_initialized)
740 WaypointSprite_Load_Frames(".tga");
741 WaypointSprite_Load_Frames(".jpg");
742 waypointsprite_initialized = true;
745 waypointsprite_count = waypointsprite_newcount;
746 waypointsprite_newcount = 0;