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)
169 if(substring(s, 0, 4) == "wpn-")
170 if(get_weaponinfo(stof(substring(s, 4, strlen(s)))).spawnflags & WEP_FLAG_SUPERWEAPON)
173 FOREACH(ITEMS, it.m_waypoint == s, LAMBDA(
174 return it.m_waypointblink;
179 case "ons-cp-atck": return 2;
180 case "ons-cp-dfnd": return 0.5;
181 case "item-invis": return 2;
182 case "item-extralife": return 2;
183 case "item-speed": return 2;
184 case "tagged-target": return 2;
188 vector spritelookupcolor(string s, vector def)
190 if(substring(s, 0, 4) == "wpn-")
191 return (get_weaponinfo(stof(substring(s, 4, strlen(s)))).wpcolor);
195 case "keycarrier-friend": return '0 1 0';
199 string spritelookuptext(string s)
201 if(substring(s, 0, 4) == "wpn-") { return (get_weaponinfo(stof(substring(s, 4, strlen(s)))).message); }
202 if (substring(s, 0, 5) == "buff-")
204 entity buff = BUFF_NULL;
205 FOREACH(BUFFS, it.m_sprite == s, LAMBDA(
209 return buff.m_prettyName;
214 case "as-push": return _("Push");
215 case "as-destroy": return _("Destroy");
216 case "as-defend": return _("Defend");
217 case "bluebase": return _("Blue base");
218 case "danger": return _("DANGER");
219 case "enemyflagcarrier": return _("Enemy carrier");
220 case "flagcarrier": return _("Flag carrier");
221 case "flagdropped": return _("Dropped flag");
222 case "helpme": return _("Help me!");
223 case "here": return _("Here");
224 case "key-dropped": return _("Dropped key");
225 case "keycarrier-blue": return _("Key carrier");
226 case "keycarrier-finish": return _("Run here");
227 case "keycarrier-friend": return _("Key carrier");
228 case "keycarrier-pink": return _("Key carrier");
229 case "keycarrier-red": return _("Key carrier");
230 case "keycarrier-yellow": return _("Key carrier");
231 case "redbase": return _("Red base");
232 case "yellowbase": return _("Yellow base");
233 case "neutralbase": return _("White base");
234 case "pinkbase": return _("Pink base");
235 case "waypoint": return _("Waypoint");
236 case "ons-gen": return _("Generator");
237 case "ons-gen-shielded": return _("Generator");
238 case "ons-cp": return _("Control point");
239 case "ons-cp-atck": return _("Control point");
240 case "ons-cp-dfnd": return _("Control point");
241 case "race-checkpoint": return _("Checkpoint");
242 case "race-finish": return _("Finish");
243 case "race-start": return _("Start");
244 case "race-start-finish": return (race_checkpointtime || race_mycheckpointtime) ? _("Finish") : _("Start");
245 case "goal": return _("Goal");
246 case "nb-ball": return _("Ball");
247 case "ka-ball": return _("Ball");
248 case "ka-ballcarrier": return _("Ball carrier");
249 case "dom-neut": return _("Control point");
250 case "dom-red": return _("Control point");
251 case "dom-blue": return _("Control point");
252 case "dom-yellow": return _("Control point");
253 case "dom-pink": return _("Control point");
254 case "item-invis": return _("Invisibility");
255 case "item-extralife": return _("Extra life");
256 case "item-speed": return _("Speed");
257 case "frozen": return _("Frozen!");
258 case "tagged-target": return _("Tagged");
259 case "vehicle": return _("Vehicle");
260 case "intruder": return _("Intruder!");
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_SPECTATOR:
354 (autocvar_g_waypointsprite_itemstime == 1 && t == NUM_SPECTATOR + 1)
355 || (autocvar_g_waypointsprite_itemstime == 2 && (t == NUM_SPECTATOR + 1 || warmup_stage))
358 spriteimage = self.netname;
360 case SPRITERULE_DEFAULT:
364 spriteimage = self.netname;
369 spriteimage = self.netname;
371 case SPRITERULE_TEAMPLAY:
372 if(t == NUM_SPECTATOR + 1)
373 spriteimage = self.netname3;
374 else if(self.team == t)
375 spriteimage = self.netname2;
377 spriteimage = self.netname;
380 error("Invalid waypointsprite rule!");
384 if(spriteimage == "")
387 ++waypointsprite_newcount;
390 dist = vlen(self.origin - view_origin);
393 a = self.alpha * autocvar_hud_panel_fg_alpha;
395 if(self.maxdistance > waypointsprite_normdistance)
396 a *= pow(bound(0, (self.maxdistance - dist) / (self.maxdistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent);
397 else if(self.maxdistance > 0)
398 a *= pow(bound(0, (waypointsprite_fadedistance - dist) / (waypointsprite_fadedistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent) * (1 - waypointsprite_minalpha) + waypointsprite_minalpha;
401 rgb = self.teamradar_color;
402 rgb = spritelookupcolor(spriteimage, rgb);
405 self.teamradar_color = '1 0 1';
406 printf("WARNING: sprite of name %s has no color, using pink so you notice it\n", spriteimage);
409 if(time - floor(time) > 0.5)
411 if(self.helpme && time < self.helpme)
412 a *= SPRITE_HELPME_BLINK;
413 else if(!self.lifetime) // fading out waypoints don't blink
414 a *= spritelookupblinkvalue(spriteimage);
426 rgb = fixrgbexcess(rgb);
431 o = project_3d_to_2d(self.origin);
433 || o.x < (vid_conwidth * waypointsprite_edgeoffset_left)
434 || o.y < (vid_conheight * waypointsprite_edgeoffset_top)
435 || o.x > (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right))
436 || o.y > (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom)))
438 // scale it to be just in view
442 d = o - '0.5 0 0' * vid_conwidth - '0 0.5 0' * vid_conheight;
443 ang = atan2(-d.x, -d.y);
447 f1 = d.x / vid_conwidth;
448 f2 = d.y / vid_conheight;
450 if(max(f1, -f1) > max(f2, -f2))
455 d = d * ((0.5 - waypointsprite_edgeoffset_right) / f1);
460 d = d * (-(0.5 - waypointsprite_edgeoffset_left) / f1);
468 d = d * ((0.5 - waypointsprite_edgeoffset_bottom) / f2);
473 d = d * (-(0.5 - waypointsprite_edgeoffset_top) / f2);
477 o = d + '0.5 0 0' * vid_conwidth + '0 0.5 0' * vid_conheight;
485 d = o - '0.5 0 0' * vid_conwidth - '0 0.5 0' * vid_conheight;
486 ang = atan2(-d.x, -d.y);
491 float edgedistance_min, crosshairdistance;
492 edgedistance_min = min((o.y - (vid_conheight * waypointsprite_edgeoffset_top)),
493 (o.x - (vid_conwidth * waypointsprite_edgeoffset_left)),
494 (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right)) - o.x,
495 (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom)) - o.y);
498 vidscale = max(vid_conwidth / vid_width, vid_conheight / vid_height);
500 crosshairdistance = sqrt( pow(o.x - vid_conwidth/2, 2) + pow(o.y - vid_conheight/2, 2) );
502 t = waypointsprite_scale * vidscale;
503 a *= waypointsprite_alpha;
506 a = a * (1 - (1 - waypointsprite_distancefadealpha) * (bound(0, dist/waypointsprite_distancefadedistance, 1)));
507 t = t * (1 - (1 - waypointsprite_distancefadescale) * (bound(0, dist/waypointsprite_distancefadedistance, 1)));
509 if (edgedistance_min < waypointsprite_edgefadedistance) {
510 a = a * (1 - (1 - waypointsprite_edgefadealpha) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1)));
511 t = t * (1 - (1 - waypointsprite_edgefadescale) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1)));
513 if(crosshairdistance < waypointsprite_crosshairfadedistance) {
514 a = a * (1 - (1 - waypointsprite_crosshairfadealpha) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1)));
515 t = t * (1 - (1 - waypointsprite_crosshairfadescale) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1)));
518 if(self.build_finished)
520 if(time < self.build_finished + 0.25)
522 if(time < self.build_started)
523 self.health = self.build_starthealth;
524 else if(time < self.build_finished)
525 self.health = (time - self.build_started) / (self.build_finished - self.build_started) * (1 - self.build_starthealth) + self.build_starthealth;
533 o = drawspritearrow(o, ang, rgb, a, SPRITE_ARROW_SCALE * t);
536 if(autocvar_g_waypointsprite_spam && waypointsprite_count >= autocvar_g_waypointsprite_spam)
539 txt = spritelookuptext(spriteimage);
540 if(self.helpme && time < self.helpme)
541 txt = sprintf(_("%s needing help!"), txt);
542 if(autocvar_g_waypointsprite_uppercase)
543 txt = strtoupper(txt);
545 draw_beginBoldFont();
548 o = drawspritetext(o, ang, (SPRITE_HEALTHBAR_WIDTH + 2 * SPRITE_HEALTHBAR_BORDER) * t, rgb, a, waypointsprite_fontsize * '1 1 0', txt);
551 if(self.build_finished)
556 marg = -(SPRITE_HEALTHBAR_MARGIN + SPRITE_HEALTHBAR_HEIGHT + 2 * SPRITE_HEALTHBAR_BORDER) * t - 0.5 * waypointsprite_fontsize;
558 marg = SPRITE_HEALTHBAR_MARGIN * t + 0.5 * waypointsprite_fontsize;
565 SPRITE_HEALTHBAR_WIDTH * t,
566 SPRITE_HEALTHBAR_HEIGHT * t,
568 SPRITE_HEALTHBAR_BORDER * t,
571 a * SPRITE_HEALTHBAR_BORDERALPHA,
573 a * SPRITE_HEALTHBAR_HEALTHALPHA,
579 o = drawspritetext(o, ang, 0, rgb, a, waypointsprite_fontsize * '1 1 0', txt);
584 void Ent_RemoveWaypointSprite()
587 strunzone(self.netname);
589 strunzone(self.netname2);
591 strunzone(self.netname3);
594 /** flags origin [team displayrule] [spritename] [spritename2] [spritename3] [lifetime maxdistance hideable] */
595 void Ent_WaypointSprite()
598 sendflags = ReadByte();
601 self.spawntime = time;
603 self.draw2d = Draw_WaypointSprite;
605 InterpolateOrigin_Undo();
606 self.iflags |= IFLAG_ORIGIN;
613 self.health = t / 191.0;
614 self.build_finished = 0;
618 t = (t - 192) * 256 + ReadByte();
619 self.build_started = servertime;
620 if(self.build_finished)
621 self.build_starthealth = bound(0, self.health, 1);
623 self.build_starthealth = 0;
624 self.build_finished = servertime + t / 32;
630 self.build_finished = 0;
635 // unfortunately, this needs to be exact (for the 3D display)
636 self.origin_x = ReadCoord();
637 self.origin_y = ReadCoord();
638 self.origin_z = ReadCoord();
639 setorigin(self, self.origin);
644 self.team = ReadByte();
645 self.rule = ReadByte();
651 strunzone(self.netname);
652 self.netname = strzone(ReadString());
658 strunzone(self.netname2);
659 self.netname2 = strzone(ReadString());
665 strunzone(self.netname3);
666 self.netname3 = strzone(ReadString());
671 self.lifetime = ReadCoord();
672 self.fadetime = ReadCoord();
673 self.maxdistance = ReadShort();
674 self.hideflags = ReadByte();
680 self.teamradar_icon = (f & 0x7F);
683 self.(teamradar_times[self.teamradar_time_index]) = time;
684 self.teamradar_time_index = (self.teamradar_time_index + 1) % MAX_TEAMRADAR_TIMES;
686 self.teamradar_color_x = ReadByte() / 255.0;
687 self.teamradar_color_y = ReadByte() / 255.0;
688 self.teamradar_color_z = ReadByte() / 255.0;
689 self.helpme = ReadByte() * 0.1;
691 self.helpme += servertime;
694 InterpolateOrigin_Note();
696 self.entremove = Ent_RemoveWaypointSprite;
699 void WaypointSprite_Load_Frames(string ext)
701 float dh, n, i, o, f;
702 string s, sname, sframes;
703 dh = search_begin(strcat("models/sprites/*_frame*", ext), false, false);
706 float ext_len = strlen(ext);
707 n = search_getsize(dh);
708 for(i = 0; i < n; ++i)
710 s = search_getfilename(dh, i);
711 s = substring(s, 15, strlen(s) - 15 - ext_len); // strip models/sprites/ and extension
713 o = strstrofs(s, "_frame", 0);
714 sname = strcat("/spriteframes/", substring(s, 0, o));
715 sframes = substring(s, o + 6, strlen(s) - o - 6);
716 f = stof(sframes) + 1;
717 db_put(tempdb, sname, ftos(max(f, stof(db_get(tempdb, sname)))));
722 void WaypointSprite_Load()
724 waypointsprite_fadedistance = vlen(mi_scale);
725 waypointsprite_normdistance = autocvar_g_waypointsprite_normdistance;
726 waypointsprite_minscale = autocvar_g_waypointsprite_minscale;
727 waypointsprite_minalpha = autocvar_g_waypointsprite_minalpha;
728 waypointsprite_distancealphaexponent = autocvar_g_waypointsprite_distancealphaexponent;
729 waypointsprite_timealphaexponent = autocvar_g_waypointsprite_timealphaexponent;
730 waypointsprite_scale = autocvar_g_waypointsprite_scale;
731 waypointsprite_fontsize = autocvar_g_waypointsprite_fontsize;
732 waypointsprite_edgefadealpha = autocvar_g_waypointsprite_edgefadealpha;
733 waypointsprite_edgefadescale = autocvar_g_waypointsprite_edgefadescale;
734 waypointsprite_edgefadedistance = autocvar_g_waypointsprite_edgefadedistance;
735 waypointsprite_edgeoffset_bottom = autocvar_g_waypointsprite_edgeoffset_bottom;
736 waypointsprite_edgeoffset_left = autocvar_g_waypointsprite_edgeoffset_left;
737 waypointsprite_edgeoffset_right = autocvar_g_waypointsprite_edgeoffset_right;
738 waypointsprite_edgeoffset_top = autocvar_g_waypointsprite_edgeoffset_top;
739 waypointsprite_crosshairfadealpha = autocvar_g_waypointsprite_crosshairfadealpha;
740 waypointsprite_crosshairfadescale = autocvar_g_waypointsprite_crosshairfadescale;
741 waypointsprite_crosshairfadedistance = autocvar_g_waypointsprite_crosshairfadedistance;
742 waypointsprite_distancefadealpha = autocvar_g_waypointsprite_distancefadealpha;
743 waypointsprite_distancefadescale = autocvar_g_waypointsprite_distancefadescale;
744 waypointsprite_distancefadedistance = waypointsprite_fadedistance * autocvar_g_waypointsprite_distancefadedistancemultiplier;
745 waypointsprite_alpha = autocvar_g_waypointsprite_alpha * (1 - autocvar__menu_alpha);
747 if(!waypointsprite_initialized)
749 WaypointSprite_Load_Frames(".tga");
750 WaypointSprite_Load_Frames(".jpg");
751 waypointsprite_initialized = true;
754 waypointsprite_count = waypointsprite_newcount;
755 waypointsprite_newcount = 0;