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");
258 case "intruder": return _("Intruder!");
263 vector fixrgbexcess_move(vector rgb, vector src, vector dst)
265 vector yvec = '0.299 0.587 0.114';
266 return rgb + dst * ((src * yvec) / (dst * yvec)) * ((rgb - '1 1 1') * src);
268 vector fixrgbexcess(vector rgb)
272 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 1 1');
275 rgb = fixrgbexcess_move(rgb, '0 1 0', '0 0 1');
281 rgb = fixrgbexcess_move(rgb, '0 0 1', '0 1 0');
288 rgb = fixrgbexcess_move(rgb, '0 1 0', '1 0 1');
291 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 0 1');
297 rgb = fixrgbexcess_move(rgb, '0 0 1', '1 0 0');
304 rgb = fixrgbexcess_move(rgb, '0 0 1', '1 1 0');
307 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 1 0');
313 rgb = fixrgbexcess_move(rgb, '0 1 0', '1 0 0');
321 void Draw_WaypointSprite()
327 self.alpha = pow(bound(0, (self.fadetime - time) / self.lifetime, 1), waypointsprite_timealphaexponent);
331 if(self.hideflags & 2)
332 return; // radar only
334 if(autocvar_cl_hidewaypoints >= 2)
337 if(self.hideflags & 1)
338 if(autocvar_cl_hidewaypoints)
339 return; // fixed waypoint
341 InterpolateOrigin_Do();
343 t = GetPlayerColor(player_localnum) + 1;
350 case SPRITERULE_DEFAULT:
354 spriteimage = self.netname;
359 spriteimage = self.netname;
361 case SPRITERULE_TEAMPLAY:
362 if(t == NUM_SPECTATOR + 1)
363 spriteimage = self.netname3;
364 else if(self.team == t)
365 spriteimage = self.netname2;
367 spriteimage = self.netname;
370 error("Invalid waypointsprite rule!");
374 if(spriteimage == "")
377 ++waypointsprite_newcount;
380 dist = vlen(self.origin - view_origin);
383 a = self.alpha * autocvar_hud_panel_fg_alpha;
385 if(self.maxdistance > waypointsprite_normdistance)
386 a *= pow(bound(0, (self.maxdistance - dist) / (self.maxdistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent);
387 else if(self.maxdistance > 0)
388 a *= pow(bound(0, (waypointsprite_fadedistance - dist) / (waypointsprite_fadedistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent) * (1 - waypointsprite_minalpha) + waypointsprite_minalpha;
391 rgb = self.teamradar_color;
392 rgb = spritelookupcolor(spriteimage, rgb);
395 self.teamradar_color = '1 0 1';
396 printf("WARNING: sprite of name %s has no color, using pink so you notice it\n", spriteimage);
399 if(time - floor(time) > 0.5)
401 if(self.helpme && time < self.helpme)
402 a *= SPRITE_HELPME_BLINK;
404 a *= spritelookupblinkvalue(spriteimage);
416 rgb = fixrgbexcess(rgb);
421 o = project_3d_to_2d(self.origin);
423 || o.x < (vid_conwidth * waypointsprite_edgeoffset_left)
424 || o.y < (vid_conheight * waypointsprite_edgeoffset_top)
425 || o.x > (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right))
426 || o.y > (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom)))
428 // scale it to be just in view
432 d = o - '0.5 0 0' * vid_conwidth - '0 0.5 0' * vid_conheight;
433 ang = atan2(-d.x, -d.y);
437 f1 = d.x / vid_conwidth;
438 f2 = d.y / vid_conheight;
440 if(max(f1, -f1) > max(f2, -f2))
445 d = d * ((0.5 - waypointsprite_edgeoffset_right) / f1);
450 d = d * (-(0.5 - waypointsprite_edgeoffset_left) / f1);
458 d = d * ((0.5 - waypointsprite_edgeoffset_bottom) / f2);
463 d = d * (-(0.5 - waypointsprite_edgeoffset_top) / f2);
467 o = d + '0.5 0 0' * vid_conwidth + '0 0.5 0' * vid_conheight;
475 d = o - '0.5 0 0' * vid_conwidth - '0 0.5 0' * vid_conheight;
476 ang = atan2(-d.x, -d.y);
481 float edgedistance_min, crosshairdistance;
482 edgedistance_min = min((o.y - (vid_conheight * waypointsprite_edgeoffset_top)),
483 (o.x - (vid_conwidth * waypointsprite_edgeoffset_left)),
484 (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right)) - o.x,
485 (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom)) - o.y);
488 vidscale = max(vid_conwidth / vid_width, vid_conheight / vid_height);
490 crosshairdistance = sqrt( pow(o.x - vid_conwidth/2, 2) + pow(o.y - vid_conheight/2, 2) );
492 t = waypointsprite_scale * vidscale;
493 a *= waypointsprite_alpha;
496 a = a * (1 - (1 - waypointsprite_distancefadealpha) * (bound(0, dist/waypointsprite_distancefadedistance, 1)));
497 t = t * (1 - (1 - waypointsprite_distancefadescale) * (bound(0, dist/waypointsprite_distancefadedistance, 1)));
499 if (edgedistance_min < waypointsprite_edgefadedistance) {
500 a = a * (1 - (1 - waypointsprite_edgefadealpha) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1)));
501 t = t * (1 - (1 - waypointsprite_edgefadescale) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1)));
503 if(crosshairdistance < waypointsprite_crosshairfadedistance) {
504 a = a * (1 - (1 - waypointsprite_crosshairfadealpha) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1)));
505 t = t * (1 - (1 - waypointsprite_crosshairfadescale) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1)));
508 if(self.build_finished)
510 if(time < self.build_finished + 0.25)
512 if(time < self.build_started)
513 self.health = self.build_starthealth;
514 else if(time < self.build_finished)
515 self.health = (time - self.build_started) / (self.build_finished - self.build_started) * (1 - self.build_starthealth) + self.build_starthealth;
523 o = drawspritearrow(o, ang, rgb, a, SPRITE_ARROW_SCALE * t);
526 if(autocvar_g_waypointsprite_spam && waypointsprite_count >= autocvar_g_waypointsprite_spam)
529 txt = spritelookuptext(spriteimage);
530 if(self.helpme && time < self.helpme)
531 txt = sprintf(_("%s needing help!"), txt);
532 if(autocvar_g_waypointsprite_uppercase)
533 txt = strtoupper(txt);
535 draw_beginBoldFont();
538 o = drawspritetext(o, ang, (SPRITE_HEALTHBAR_WIDTH + 2 * SPRITE_HEALTHBAR_BORDER) * t, rgb, a, waypointsprite_fontsize * '1 1 0', txt);
541 if(self.build_finished)
546 marg = -(SPRITE_HEALTHBAR_MARGIN + SPRITE_HEALTHBAR_HEIGHT + 2 * SPRITE_HEALTHBAR_BORDER) * t - 0.5 * waypointsprite_fontsize;
548 marg = SPRITE_HEALTHBAR_MARGIN * t + 0.5 * waypointsprite_fontsize;
555 SPRITE_HEALTHBAR_WIDTH * t,
556 SPRITE_HEALTHBAR_HEIGHT * t,
558 SPRITE_HEALTHBAR_BORDER * t,
561 a * SPRITE_HEALTHBAR_BORDERALPHA,
563 a * SPRITE_HEALTHBAR_HEALTHALPHA,
569 o = drawspritetext(o, ang, 0, rgb, a, waypointsprite_fontsize * '1 1 0', txt);
574 void Ent_RemoveWaypointSprite()
577 strunzone(self.netname);
579 strunzone(self.netname2);
581 strunzone(self.netname3);
584 void Ent_WaypointSprite()
587 sendflags = ReadByte();
590 self.spawntime = time;
592 self.draw2d = Draw_WaypointSprite;
594 InterpolateOrigin_Undo();
595 self.iflags |= IFLAG_ORIGIN;
602 self.health = t / 191.0;
603 self.build_finished = 0;
607 t = (t - 192) * 256 + ReadByte();
608 self.build_started = servertime;
609 if(self.build_finished)
610 self.build_starthealth = bound(0, self.health, 1);
612 self.build_starthealth = 0;
613 self.build_finished = servertime + t / 32;
619 self.build_finished = 0;
624 // unfortunately, this needs to be exact (for the 3D display)
625 self.origin_x = ReadCoord();
626 self.origin_y = ReadCoord();
627 self.origin_z = ReadCoord();
628 setorigin(self, self.origin);
633 self.team = ReadByte();
634 self.rule = ReadByte();
640 strunzone(self.netname);
641 self.netname = strzone(ReadString());
647 strunzone(self.netname2);
648 self.netname2 = strzone(ReadString());
654 strunzone(self.netname3);
655 self.netname3 = strzone(ReadString());
660 self.lifetime = ReadCoord();
661 self.fadetime = ReadCoord();
662 self.maxdistance = ReadShort();
663 self.hideflags = ReadByte();
669 self.teamradar_icon = (f & 0x7F);
672 self.(teamradar_times[self.teamradar_time_index]) = time;
673 self.teamradar_time_index = (self.teamradar_time_index + 1) % MAX_TEAMRADAR_TIMES;
675 self.teamradar_color_x = ReadByte() / 255.0;
676 self.teamradar_color_y = ReadByte() / 255.0;
677 self.teamradar_color_z = ReadByte() / 255.0;
678 self.helpme = ReadByte() * 0.1;
680 self.helpme += servertime;
683 InterpolateOrigin_Note();
685 self.entremove = Ent_RemoveWaypointSprite;
688 void WaypointSprite_Load_Frames(string ext)
690 float dh, n, i, o, f;
691 string s, sname, sframes;
692 dh = search_begin(strcat("models/sprites/*_frame*", ext), false, false);
695 float ext_len = strlen(ext);
696 n = search_getsize(dh);
697 for(i = 0; i < n; ++i)
699 s = search_getfilename(dh, i);
700 s = substring(s, 15, strlen(s) - 15 - ext_len); // strip models/sprites/ and extension
702 o = strstrofs(s, "_frame", 0);
703 sname = strcat("/spriteframes/", substring(s, 0, o));
704 sframes = substring(s, o + 6, strlen(s) - o - 6);
705 f = stof(sframes) + 1;
706 db_put(tempdb, sname, ftos(max(f, stof(db_get(tempdb, sname)))));
711 void WaypointSprite_Load()
713 waypointsprite_fadedistance = vlen(mi_scale);
714 waypointsprite_normdistance = autocvar_g_waypointsprite_normdistance;
715 waypointsprite_minscale = autocvar_g_waypointsprite_minscale;
716 waypointsprite_minalpha = autocvar_g_waypointsprite_minalpha;
717 waypointsprite_distancealphaexponent = autocvar_g_waypointsprite_distancealphaexponent;
718 waypointsprite_timealphaexponent = autocvar_g_waypointsprite_timealphaexponent;
719 waypointsprite_scale = autocvar_g_waypointsprite_scale;
720 waypointsprite_fontsize = autocvar_g_waypointsprite_fontsize;
721 waypointsprite_edgefadealpha = autocvar_g_waypointsprite_edgefadealpha;
722 waypointsprite_edgefadescale = autocvar_g_waypointsprite_edgefadescale;
723 waypointsprite_edgefadedistance = autocvar_g_waypointsprite_edgefadedistance;
724 waypointsprite_edgeoffset_bottom = autocvar_g_waypointsprite_edgeoffset_bottom;
725 waypointsprite_edgeoffset_left = autocvar_g_waypointsprite_edgeoffset_left;
726 waypointsprite_edgeoffset_right = autocvar_g_waypointsprite_edgeoffset_right;
727 waypointsprite_edgeoffset_top = autocvar_g_waypointsprite_edgeoffset_top;
728 waypointsprite_crosshairfadealpha = autocvar_g_waypointsprite_crosshairfadealpha;
729 waypointsprite_crosshairfadescale = autocvar_g_waypointsprite_crosshairfadescale;
730 waypointsprite_crosshairfadedistance = autocvar_g_waypointsprite_crosshairfadedistance;
731 waypointsprite_distancefadealpha = autocvar_g_waypointsprite_distancefadealpha;
732 waypointsprite_distancefadescale = autocvar_g_waypointsprite_distancefadescale;
733 waypointsprite_distancefadedistance = waypointsprite_fadedistance * autocvar_g_waypointsprite_distancefadedistancemultiplier;
734 waypointsprite_alpha = autocvar_g_waypointsprite_alpha * (1 - autocvar__menu_alpha);
736 if(!waypointsprite_initialized)
738 WaypointSprite_Load_Frames(".tga");
739 WaypointSprite_Load_Frames(".jpg");
740 waypointsprite_initialized = true;
743 waypointsprite_count = waypointsprite_newcount;
744 waypointsprite_newcount = 0;