1 #include "waypointsprites.qh"
5 #include "teamradar.qh"
7 #include "../common/buffs.qh"
8 #include "../common/constants.qh"
9 #include "../common/teams.qh"
11 #include "../common/weapons/all.qh"
13 #include "../csqcmodellib/interpolate.qh"
15 #include "../warpzonelib/mathlib.qh"
19 void drawrotpic(vector org, float rot, string pic, vector sz, vector hotspot, vector rgb, float a, float f)
21 vector v1, v2, v3, v4;
23 hotspot = -1 * hotspot;
25 // hotspot-relative coordinates of the corners
27 v2 = hotspot + '1 0 0' * sz.x;
28 v3 = hotspot + '1 0 0' * sz.x + '0 1 0' * sz.y;
29 v4 = hotspot + '0 1 0' * sz.y;
31 // rotate them, and make them absolute
32 rot = -rot; // rotate by the opposite angle, as our coordinate system is reversed
33 v1 = rotate(v1, rot) + org;
34 v2 = rotate(v2, rot) + org;
35 v3 = rotate(v3, rot) + org;
36 v4 = rotate(v4, rot) + org;
39 R_BeginPolygon(pic, f);
40 R_PolygonVertex(v1, '0 0 0', rgb, a);
41 R_PolygonVertex(v2, '1 0 0', rgb, a);
42 R_PolygonVertex(v3, '1 1 0', rgb, a);
43 R_PolygonVertex(v4, '0 1 0', rgb, a);
47 void drawquad(vector o, vector ri, vector up, string pic, vector rgb, float a, float f)
49 R_BeginPolygon(pic, f);
50 R_PolygonVertex(o, '0 0 0', rgb, a);
51 R_PolygonVertex(o + ri, '1 0 0', rgb, a);
52 R_PolygonVertex(o + up + ri, '1 1 0', rgb, a);
53 R_PolygonVertex(o + up, '0 1 0', rgb, a);
57 void drawhealthbar(vector org, float rot, float h, vector sz, vector hotspot, float width, float theheight, float margin, float border, float align, vector rgb, float a, vector hrgb, float ha, float f)
60 float owidth; // outer width
62 hotspot = -1 * hotspot;
64 // hotspot-relative coordinates of the healthbar corners
69 rot = -rot; // rotate by the opposite angle, as our coordinate system is reversed
70 o = rotate(o, rot) + org;
74 owidth = width + 2 * border;
75 o = o - up * (margin + border + theheight) + ri * (sz.x - owidth) * 0.5;
77 drawquad(o - up * border, ri * owidth, up * border, "", rgb, a, f);
78 drawquad(o + up * theheight, ri * owidth, up * border, "", rgb, a, f);
79 drawquad(o, ri * border, up * theheight, "", rgb, a, f);
80 drawquad(o + ri * (owidth - border), ri * border, up * theheight, "", rgb, a, f);
81 drawquad(o + ri * (border + align * ((1 - h) * width)), ri * width * h, up * theheight, "", hrgb, ha, f);
84 // returns location of sprite text
85 vector drawspritearrow(vector o, float ang, vector rgb, float a, float t)
88 float border = 1.5 * t;
89 float margin = 4.0 * t;
91 float borderDiag = border * 1.414;
92 vector arrowX = eX * size;
93 vector arrowY = eY * (size+borderDiag);
94 vector borderX = eX * (size+borderDiag);
95 vector borderY = eY * (size+borderDiag+border);
97 R_BeginPolygon("", DRAWFLAG_NORMAL);
98 R_PolygonVertex(o, '0 0 0', '0 0 0', a);
99 R_PolygonVertex(o + rotate(arrowY - 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(borderY + borderX, ang), '0 0 0', '0 0 0', a);
102 R_PolygonVertex(o + rotate(arrowY + borderX, ang), '0 0 0', '0 0 0', a);
105 R_BeginPolygon("", DRAWFLAG_ADDITIVE);
106 R_PolygonVertex(o + rotate(eY * borderDiag, ang), '0 0 0', rgb, a);
107 R_PolygonVertex(o + rotate(arrowY - arrowX, ang), '0 0 0', rgb, a);
108 R_PolygonVertex(o + rotate(arrowY + arrowX, ang), '0 0 0', rgb, a);
111 return o + rotate(eY * (borderDiag+size+margin), ang);
114 // returns location of sprite healthbar
115 vector drawspritetext(vector o, float ang, float minwidth, vector rgb, float a, vector fontsize, string s)
119 float aspect, sa, ca;
121 sw = stringwidth(s, false, fontsize);
128 // how do corners work?
129 aspect = vid_conwidth / vid_conheight;
131 ca = cos(ang) * aspect;
132 if(fabs(sa) > fabs(ca))
135 algny = 0.5 - 0.5 * ca / fabs(sa);
139 algnx = 0.5 - 0.5 * sa / fabs(ca);
147 // we want to be onscreen
152 if(o.x > vid_conwidth - w)
153 o.x = vid_conwidth - w;
154 if(o.y > vid_conheight - h)
155 o.x = vid_conheight - h;
157 o.x += 0.5 * (w - sw);
159 drawstring(o, s, fontsize, rgb, a, DRAWFLAG_NORMAL);
167 float spritelookupblinkvalue(string s)
171 case "ons-cp-atck-neut": return 2;
172 case "ons-cp-atck-red": return 2;
173 case "ons-cp-atck-blue": return 2;
174 case "ons-cp-dfnd-red": return 0.5;
175 case "ons-cp-dfnd-blue": return 0.5;
176 case "item-invis": return 2;
177 case "item-extralife": return 2;
178 case "item-speed": return 2;
179 case "item-strength": return 2;
180 case "item-shield": return 2;
181 case "item-fuelregen": return 2;
182 case "item-jetpack": return 2;
183 case "tagged-target": return 2;
187 vector spritelookupcolor(string s, vector def)
189 if(substring(s, 0, 4) == "wpn-")
190 return (get_weaponinfo(stof(substring(s, 4, strlen(s)))).wpcolor);
194 case "keycarrier-friend": return '0 1 0';
198 string spritelookuptext(string s)
200 if(substring(s, 0, 4) == "wpn-") { return (get_weaponinfo(stof(substring(s, 4, strlen(s)))).message); }
201 if(substring(s, 0, 5) == "buff-") { return Buff_PrettyName(Buff_Type_FromSprite(s)); }
205 case "as-push": return _("Push");
206 case "as-destroy": return _("Destroy");
207 case "as-defend": return _("Defend");
208 case "bluebase": return _("Blue base");
209 case "danger": return _("DANGER");
210 case "enemyflagcarrier": return _("Enemy carrier");
211 case "flagcarrier": return _("Flag carrier");
212 case "flagdropped": return _("Dropped flag");
213 case "helpme": return _("Help me!");
214 case "here": return _("Here");
215 case "key-dropped": return _("Dropped key");
216 case "keycarrier-blue": return _("Key carrier");
217 case "keycarrier-finish": return _("Run here");
218 case "keycarrier-friend": return _("Key carrier");
219 case "keycarrier-pink": return _("Key carrier");
220 case "keycarrier-red": return _("Key carrier");
221 case "keycarrier-yellow": return _("Key carrier");
222 case "redbase": return _("Red base");
223 case "waypoint": return _("Waypoint");
224 case "ons-gen-red": return _("Generator");
225 case "ons-gen-blue": return _("Generator");
226 case "ons-gen-shielded": return _("Generator");
227 case "ons-cp-neut": return _("Control point");
228 case "ons-cp-red": return _("Control point");
229 case "ons-cp-blue": return _("Control point");
230 case "ons-cp-atck-neut": return _("Control point");
231 case "ons-cp-atck-red": return _("Control point");
232 case "ons-cp-atck-blue": return _("Control point");
233 case "ons-cp-dfnd-red": return _("Control point");
234 case "ons-cp-dfnd-blue": return _("Control point");
235 case "race-checkpoint": return _("Checkpoint");
236 case "race-finish": return _("Finish");
237 case "race-start": return _("Start");
238 case "race-start-finish": return (race_checkpointtime || race_mycheckpointtime) ? _("Finish") : _("Start");
239 case "goal": return _("Goal");
240 case "nb-ball": return _("Ball");
241 case "ka-ball": return _("Ball");
242 case "ka-ballcarrier": return _("Ball carrier");
243 case "dom-neut": return _("Control point");
244 case "dom-red": return _("Control point");
245 case "dom-blue": return _("Control point");
246 case "dom-yellow": return _("Control point");
247 case "dom-pink": return _("Control point");
248 case "item-invis": return _("Invisibility");
249 case "item-extralife": return _("Extra life");
250 case "item-speed": return _("Speed");
251 case "item-strength": return _("Strength");
252 case "item-shield": return _("Shield");
253 case "item-fuelregen": return _("Fuel regen");
254 case "item-jetpack": return _("Jet Pack");
255 case "frozen": return _("Frozen!");
256 case "tagged-target": return _("Tagged");
257 case "vehicle": return _("Vehicle");
262 vector fixrgbexcess_move(vector rgb, vector src, vector dst)
264 vector yvec = '0.299 0.587 0.114';
265 return rgb + dst * ((src * yvec) / (dst * yvec)) * ((rgb - '1 1 1') * src);
267 vector fixrgbexcess(vector rgb)
271 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 1 1');
274 rgb = fixrgbexcess_move(rgb, '0 1 0', '0 0 1');
280 rgb = fixrgbexcess_move(rgb, '0 0 1', '0 1 0');
287 rgb = fixrgbexcess_move(rgb, '0 1 0', '1 0 1');
290 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 0 1');
296 rgb = fixrgbexcess_move(rgb, '0 0 1', '1 0 0');
303 rgb = fixrgbexcess_move(rgb, '0 0 1', '1 1 0');
306 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 1 0');
312 rgb = fixrgbexcess_move(rgb, '0 1 0', '1 0 0');
320 void Draw_WaypointSprite()
326 self.alpha = pow(bound(0, (self.fadetime - time) / self.lifetime, 1), waypointsprite_timealphaexponent);
330 if(self.hideflags & 2)
331 return; // radar only
333 if(autocvar_cl_hidewaypoints >= 2)
336 if(self.hideflags & 1)
337 if(autocvar_cl_hidewaypoints)
338 return; // fixed waypoint
340 InterpolateOrigin_Do();
342 t = GetPlayerColor(player_localnum) + 1;
349 case SPRITERULE_DEFAULT:
353 spriteimage = self.netname;
358 spriteimage = self.netname;
360 case SPRITERULE_TEAMPLAY:
361 if(t == NUM_SPECTATOR + 1)
362 spriteimage = self.netname3;
363 else if(self.team == t)
364 spriteimage = self.netname2;
366 spriteimage = self.netname;
369 error("Invalid waypointsprite rule!");
373 if(spriteimage == "")
376 ++waypointsprite_newcount;
379 dist = vlen(self.origin - view_origin);
382 a = self.alpha * autocvar_hud_panel_fg_alpha;
384 if(self.maxdistance > waypointsprite_normdistance)
385 a *= pow(bound(0, (self.maxdistance - dist) / (self.maxdistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent);
386 else if(self.maxdistance > 0)
387 a *= pow(bound(0, (waypointsprite_fadedistance - dist) / (waypointsprite_fadedistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent) * (1 - waypointsprite_minalpha) + waypointsprite_minalpha;
390 rgb = self.teamradar_color;
391 rgb = spritelookupcolor(spriteimage, rgb);
394 self.teamradar_color = '1 0 1';
395 printf("WARNING: sprite of name %s has no color, using pink so you notice it\n", spriteimage);
398 if(time - floor(time) > 0.5)
400 if(self.helpme && time < self.helpme)
401 a *= SPRITE_HELPME_BLINK;
403 a *= spritelookupblinkvalue(spriteimage);
415 rgb = fixrgbexcess(rgb);
420 o = project_3d_to_2d(self.origin);
422 || o.x < (vid_conwidth * waypointsprite_edgeoffset_left)
423 || o.y < (vid_conheight * waypointsprite_edgeoffset_top)
424 || o.x > (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right))
425 || o.y > (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom)))
427 // scale it to be just in view
431 d = o - '0.5 0 0' * vid_conwidth - '0 0.5 0' * vid_conheight;
432 ang = atan2(-d.x, -d.y);
436 f1 = d.x / vid_conwidth;
437 f2 = d.y / vid_conheight;
439 if(max(f1, -f1) > max(f2, -f2))
444 d = d * ((0.5 - waypointsprite_edgeoffset_right) / f1);
449 d = d * (-(0.5 - waypointsprite_edgeoffset_left) / f1);
457 d = d * ((0.5 - waypointsprite_edgeoffset_bottom) / f2);
462 d = d * (-(0.5 - waypointsprite_edgeoffset_top) / f2);
466 o = d + '0.5 0 0' * vid_conwidth + '0 0.5 0' * vid_conheight;
474 d = o - '0.5 0 0' * vid_conwidth - '0 0.5 0' * vid_conheight;
475 ang = atan2(-d.x, -d.y);
480 float edgedistance_min, crosshairdistance;
481 edgedistance_min = min((o.y - (vid_conheight * waypointsprite_edgeoffset_top)),
482 (o.x - (vid_conwidth * waypointsprite_edgeoffset_left)),
483 (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right)) - o.x,
484 (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom)) - o.y);
487 vidscale = max(vid_conwidth / vid_width, vid_conheight / vid_height);
489 crosshairdistance = sqrt( pow(o.x - vid_conwidth/2, 2) + pow(o.y - vid_conheight/2, 2) );
491 t = waypointsprite_scale * vidscale;
492 a *= waypointsprite_alpha;
495 a = a * (1 - (1 - waypointsprite_distancefadealpha) * (bound(0, dist/waypointsprite_distancefadedistance, 1)));
496 t = t * (1 - (1 - waypointsprite_distancefadescale) * (bound(0, dist/waypointsprite_distancefadedistance, 1)));
498 if (edgedistance_min < waypointsprite_edgefadedistance) {
499 a = a * (1 - (1 - waypointsprite_edgefadealpha) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1)));
500 t = t * (1 - (1 - waypointsprite_edgefadescale) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1)));
502 if(crosshairdistance < waypointsprite_crosshairfadedistance) {
503 a = a * (1 - (1 - waypointsprite_crosshairfadealpha) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1)));
504 t = t * (1 - (1 - waypointsprite_crosshairfadescale) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1)));
507 if(self.build_finished)
509 if(time < self.build_finished + 0.25)
511 if(time < self.build_started)
512 self.health = self.build_starthealth;
513 else if(time < self.build_finished)
514 self.health = (time - self.build_started) / (self.build_finished - self.build_started) * (1 - self.build_starthealth) + self.build_starthealth;
522 o = drawspritearrow(o, ang, rgb, a, SPRITE_ARROW_SCALE * t);
525 if(autocvar_g_waypointsprite_spam && waypointsprite_count >= autocvar_g_waypointsprite_spam)
528 txt = spritelookuptext(spriteimage);
529 if(self.helpme && time < self.helpme)
530 txt = sprintf(_("%s needing help!"), txt);
531 if(autocvar_g_waypointsprite_uppercase)
532 txt = strtoupper(txt);
534 draw_beginBoldFont();
537 o = drawspritetext(o, ang, (SPRITE_HEALTHBAR_WIDTH + 2 * SPRITE_HEALTHBAR_BORDER) * t, rgb, a, waypointsprite_fontsize * '1 1 0', txt);
540 if(self.build_finished)
545 marg = -(SPRITE_HEALTHBAR_MARGIN + SPRITE_HEALTHBAR_HEIGHT + 2 * SPRITE_HEALTHBAR_BORDER) * t - 0.5 * waypointsprite_fontsize;
547 marg = SPRITE_HEALTHBAR_MARGIN * t + 0.5 * waypointsprite_fontsize;
554 SPRITE_HEALTHBAR_WIDTH * t,
555 SPRITE_HEALTHBAR_HEIGHT * t,
557 SPRITE_HEALTHBAR_BORDER * t,
560 a * SPRITE_HEALTHBAR_BORDERALPHA,
562 a * SPRITE_HEALTHBAR_HEALTHALPHA,
568 o = drawspritetext(o, ang, 0, rgb, a, waypointsprite_fontsize * '1 1 0', txt);
573 void Ent_RemoveWaypointSprite()
576 strunzone(self.netname);
578 strunzone(self.netname2);
580 strunzone(self.netname3);
583 void Ent_WaypointSprite()
586 sendflags = ReadByte();
589 self.spawntime = time;
591 self.draw2d = Draw_WaypointSprite;
593 InterpolateOrigin_Undo();
594 self.iflags |= IFLAG_ORIGIN;
601 self.health = t / 191.0;
602 self.build_finished = 0;
606 t = (t - 192) * 256 + ReadByte();
607 self.build_started = servertime;
608 if(self.build_finished)
609 self.build_starthealth = bound(0, self.health, 1);
611 self.build_starthealth = 0;
612 self.build_finished = servertime + t / 32;
618 self.build_finished = 0;
623 // unfortunately, this needs to be exact (for the 3D display)
624 self.origin_x = ReadCoord();
625 self.origin_y = ReadCoord();
626 self.origin_z = ReadCoord();
627 setorigin(self, self.origin);
632 self.team = ReadByte();
633 self.rule = ReadByte();
639 strunzone(self.netname);
640 self.netname = strzone(ReadString());
646 strunzone(self.netname2);
647 self.netname2 = strzone(ReadString());
653 strunzone(self.netname3);
654 self.netname3 = strzone(ReadString());
659 self.lifetime = ReadCoord();
660 self.fadetime = ReadCoord();
661 self.maxdistance = ReadShort();
662 self.hideflags = ReadByte();
668 self.teamradar_icon = (f & 0x7F);
671 self.(teamradar_times[self.teamradar_time_index]) = time;
672 self.teamradar_time_index = (self.teamradar_time_index + 1) % MAX_TEAMRADAR_TIMES;
674 self.teamradar_color_x = ReadByte() / 255.0;
675 self.teamradar_color_y = ReadByte() / 255.0;
676 self.teamradar_color_z = ReadByte() / 255.0;
677 self.helpme = ReadByte() * 0.1;
679 self.helpme += servertime;
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_edgeoffset_bottom = autocvar_g_waypointsprite_edgeoffset_bottom;
724 waypointsprite_edgeoffset_left = autocvar_g_waypointsprite_edgeoffset_left;
725 waypointsprite_edgeoffset_right = autocvar_g_waypointsprite_edgeoffset_right;
726 waypointsprite_edgeoffset_top = autocvar_g_waypointsprite_edgeoffset_top;
727 waypointsprite_crosshairfadealpha = autocvar_g_waypointsprite_crosshairfadealpha;
728 waypointsprite_crosshairfadescale = autocvar_g_waypointsprite_crosshairfadescale;
729 waypointsprite_crosshairfadedistance = autocvar_g_waypointsprite_crosshairfadedistance;
730 waypointsprite_distancefadealpha = autocvar_g_waypointsprite_distancefadealpha;
731 waypointsprite_distancefadescale = autocvar_g_waypointsprite_distancefadescale;
732 waypointsprite_distancefadedistance = waypointsprite_fadedistance * autocvar_g_waypointsprite_distancefadedistancemultiplier;
733 waypointsprite_alpha = autocvar_g_waypointsprite_alpha * (1 - autocvar__menu_alpha);
735 if(!waypointsprite_initialized)
737 WaypointSprite_Load_Frames(".tga");
738 WaypointSprite_Load_Frames(".jpg");
739 waypointsprite_initialized = true;
742 waypointsprite_count = waypointsprite_newcount;
743 waypointsprite_newcount = 0;