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)
175 case "ons-cp-atck-neut": return 2;
176 case "ons-cp-atck-red": return 2;
177 case "ons-cp-atck-blue": return 2;
178 case "ons-cp-dfnd-red": return 0.5;
179 case "ons-cp-dfnd-blue": return 0.5;
180 case "item_health_mega": return 2;
181 case "item_armor_large": return 2;
182 case "item-invis": return 2;
183 case "item-extralife": return 2;
184 case "item-speed": return 2;
185 case "item-strength": return 2;
186 case "item-shield": return 2;
187 case "item-fuelregen": return 2;
188 case "item-jetpack": return 2;
189 case "tagged-target": return 2;
193 vector spritelookupcolor(string s, vector def)
195 if(substring(s, 0, 4) == "wpn-")
196 return (get_weaponinfo(stof(substring(s, 4, strlen(s)))).wpcolor);
200 case "keycarrier-friend": return '0 1 0';
204 string spritelookuptext(string s)
206 if(substring(s, 0, 4) == "wpn-") { return (get_weaponinfo(stof(substring(s, 4, strlen(s)))).message); }
207 if (substring(s, 0, 5) == "buff-")
209 entity buff = BUFF_NULL;
210 FOREACH(BUFFS, it.m_sprite == s, LAMBDA(
214 return buff.m_prettyName;
219 case "as-push": return _("Push");
220 case "as-destroy": return _("Destroy");
221 case "as-defend": return _("Defend");
222 case "bluebase": return _("Blue base");
223 case "danger": return _("DANGER");
224 case "enemyflagcarrier": return _("Enemy carrier");
225 case "flagcarrier": return _("Flag carrier");
226 case "flagdropped": return _("Dropped flag");
227 case "helpme": return _("Help me!");
228 case "here": return _("Here");
229 case "key-dropped": return _("Dropped key");
230 case "keycarrier-blue": return _("Key carrier");
231 case "keycarrier-finish": return _("Run here");
232 case "keycarrier-friend": return _("Key carrier");
233 case "keycarrier-pink": return _("Key carrier");
234 case "keycarrier-red": return _("Key carrier");
235 case "keycarrier-yellow": return _("Key carrier");
236 case "redbase": return _("Red base");
237 case "yellowbase": return _("Yellow base");
238 case "neutralbase": return _("White base");
239 case "pinkbase": return _("Pink base");
240 case "waypoint": return _("Waypoint");
241 case "ons-gen-red": return _("Generator");
242 case "ons-gen-blue": return _("Generator");
243 case "ons-gen-shielded": return _("Generator");
244 case "ons-cp-neut": return _("Control point");
245 case "ons-cp-red": return _("Control point");
246 case "ons-cp-blue": return _("Control point");
247 case "ons-cp-atck-neut": return _("Control point");
248 case "ons-cp-atck-red": return _("Control point");
249 case "ons-cp-atck-blue": return _("Control point");
250 case "ons-cp-dfnd-red": return _("Control point");
251 case "ons-cp-dfnd-blue": return _("Control point");
252 case "race-checkpoint": return _("Checkpoint");
253 case "race-finish": return _("Finish");
254 case "race-start": return _("Start");
255 case "race-start-finish": return (race_checkpointtime || race_mycheckpointtime) ? _("Finish") : _("Start");
256 case "goal": return _("Goal");
257 case "nb-ball": return _("Ball");
258 case "ka-ball": return _("Ball");
259 case "ka-ballcarrier": return _("Ball carrier");
260 case "dom-neut": return _("Control point");
261 case "dom-red": return _("Control point");
262 case "dom-blue": return _("Control point");
263 case "dom-yellow": return _("Control point");
264 case "dom-pink": return _("Control point");
265 case "item_health_mega": return _("Mega health");
266 case "item_armor_large": return _("Large armor");
267 case "item-invis": return _("Invisibility");
268 case "item-extralife": return _("Extra life");
269 case "item-speed": return _("Speed");
270 case "item-strength": return _("Strength");
271 case "item-shield": return _("Shield");
272 case "item-fuelregen": return _("Fuel regen");
273 case "item-jetpack": return _("Jet Pack");
274 case "frozen": return _("Frozen!");
275 case "tagged-target": return _("Tagged");
276 case "vehicle": return _("Vehicle");
277 case "intruder": return _("Intruder!");
282 vector fixrgbexcess_move(vector rgb, vector src, vector dst)
284 vector yvec = '0.299 0.587 0.114';
285 return rgb + dst * ((src * yvec) / (dst * yvec)) * ((rgb - '1 1 1') * src);
287 vector fixrgbexcess(vector rgb)
291 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 1 1');
294 rgb = fixrgbexcess_move(rgb, '0 1 0', '0 0 1');
300 rgb = fixrgbexcess_move(rgb, '0 0 1', '0 1 0');
307 rgb = fixrgbexcess_move(rgb, '0 1 0', '1 0 1');
310 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 0 1');
316 rgb = fixrgbexcess_move(rgb, '0 0 1', '1 0 0');
323 rgb = fixrgbexcess_move(rgb, '0 0 1', '1 1 0');
326 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 1 0');
332 rgb = fixrgbexcess_move(rgb, '0 1 0', '1 0 0');
340 void Draw_WaypointSprite()
346 self.alpha = pow(bound(0, (self.fadetime - time) / self.lifetime, 1), waypointsprite_timealphaexponent);
350 if(self.hideflags & 2)
351 return; // radar only
353 if(autocvar_cl_hidewaypoints >= 2)
356 if(self.hideflags & 1)
357 if(autocvar_cl_hidewaypoints)
358 return; // fixed waypoint
360 InterpolateOrigin_Do();
362 t = GetPlayerColor(player_localnum) + 1;
369 case SPRITERULE_SPECTATOR:
371 (autocvar_g_waypointsprite_itemstime == 1 && t == NUM_SPECTATOR + 1)
372 || (autocvar_g_waypointsprite_itemstime == 2 && (t == NUM_SPECTATOR + 1 || warmup_stage))
375 spriteimage = self.netname;
377 case SPRITERULE_DEFAULT:
381 spriteimage = self.netname;
386 spriteimage = self.netname;
388 case SPRITERULE_TEAMPLAY:
389 if(t == NUM_SPECTATOR + 1)
390 spriteimage = self.netname3;
391 else if(self.team == t)
392 spriteimage = self.netname2;
394 spriteimage = self.netname;
397 error("Invalid waypointsprite rule!");
401 if(spriteimage == "")
404 ++waypointsprite_newcount;
407 dist = vlen(self.origin - view_origin);
410 a = self.alpha * autocvar_hud_panel_fg_alpha;
412 if(self.maxdistance > waypointsprite_normdistance)
413 a *= pow(bound(0, (self.maxdistance - dist) / (self.maxdistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent);
414 else if(self.maxdistance > 0)
415 a *= pow(bound(0, (waypointsprite_fadedistance - dist) / (waypointsprite_fadedistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent) * (1 - waypointsprite_minalpha) + waypointsprite_minalpha;
418 rgb = self.teamradar_color;
419 rgb = spritelookupcolor(spriteimage, rgb);
422 self.teamradar_color = '1 0 1';
423 printf("WARNING: sprite of name %s has no color, using pink so you notice it\n", spriteimage);
426 if(time - floor(time) > 0.5)
428 if(self.helpme && time < self.helpme)
429 a *= SPRITE_HELPME_BLINK;
430 else if(!self.lifetime) // fading out waypoints don't blink
431 a *= spritelookupblinkvalue(spriteimage);
443 rgb = fixrgbexcess(rgb);
448 o = project_3d_to_2d(self.origin);
450 || o.x < (vid_conwidth * waypointsprite_edgeoffset_left)
451 || o.y < (vid_conheight * waypointsprite_edgeoffset_top)
452 || o.x > (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right))
453 || o.y > (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom)))
455 // scale it to be just in view
459 d = o - '0.5 0 0' * vid_conwidth - '0 0.5 0' * vid_conheight;
460 ang = atan2(-d.x, -d.y);
464 f1 = d.x / vid_conwidth;
465 f2 = d.y / vid_conheight;
467 if(max(f1, -f1) > max(f2, -f2))
472 d = d * ((0.5 - waypointsprite_edgeoffset_right) / f1);
477 d = d * (-(0.5 - waypointsprite_edgeoffset_left) / f1);
485 d = d * ((0.5 - waypointsprite_edgeoffset_bottom) / f2);
490 d = d * (-(0.5 - waypointsprite_edgeoffset_top) / f2);
494 o = d + '0.5 0 0' * vid_conwidth + '0 0.5 0' * vid_conheight;
502 d = o - '0.5 0 0' * vid_conwidth - '0 0.5 0' * vid_conheight;
503 ang = atan2(-d.x, -d.y);
508 float edgedistance_min, crosshairdistance;
509 edgedistance_min = min((o.y - (vid_conheight * waypointsprite_edgeoffset_top)),
510 (o.x - (vid_conwidth * waypointsprite_edgeoffset_left)),
511 (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right)) - o.x,
512 (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom)) - o.y);
515 vidscale = max(vid_conwidth / vid_width, vid_conheight / vid_height);
517 crosshairdistance = sqrt( pow(o.x - vid_conwidth/2, 2) + pow(o.y - vid_conheight/2, 2) );
519 t = waypointsprite_scale * vidscale;
520 a *= waypointsprite_alpha;
523 a = a * (1 - (1 - waypointsprite_distancefadealpha) * (bound(0, dist/waypointsprite_distancefadedistance, 1)));
524 t = t * (1 - (1 - waypointsprite_distancefadescale) * (bound(0, dist/waypointsprite_distancefadedistance, 1)));
526 if (edgedistance_min < waypointsprite_edgefadedistance) {
527 a = a * (1 - (1 - waypointsprite_edgefadealpha) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1)));
528 t = t * (1 - (1 - waypointsprite_edgefadescale) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1)));
530 if(crosshairdistance < waypointsprite_crosshairfadedistance) {
531 a = a * (1 - (1 - waypointsprite_crosshairfadealpha) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1)));
532 t = t * (1 - (1 - waypointsprite_crosshairfadescale) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1)));
535 if(self.build_finished)
537 if(time < self.build_finished + 0.25)
539 if(time < self.build_started)
540 self.health = self.build_starthealth;
541 else if(time < self.build_finished)
542 self.health = (time - self.build_started) / (self.build_finished - self.build_started) * (1 - self.build_starthealth) + self.build_starthealth;
550 o = drawspritearrow(o, ang, rgb, a, SPRITE_ARROW_SCALE * t);
553 if(autocvar_g_waypointsprite_spam && waypointsprite_count >= autocvar_g_waypointsprite_spam)
556 txt = spritelookuptext(spriteimage);
557 if(self.helpme && time < self.helpme)
558 txt = sprintf(_("%s needing help!"), txt);
559 if(autocvar_g_waypointsprite_uppercase)
560 txt = strtoupper(txt);
562 draw_beginBoldFont();
565 o = drawspritetext(o, ang, (SPRITE_HEALTHBAR_WIDTH + 2 * SPRITE_HEALTHBAR_BORDER) * t, rgb, a, waypointsprite_fontsize * '1 1 0', txt);
568 if(self.build_finished)
573 marg = -(SPRITE_HEALTHBAR_MARGIN + SPRITE_HEALTHBAR_HEIGHT + 2 * SPRITE_HEALTHBAR_BORDER) * t - 0.5 * waypointsprite_fontsize;
575 marg = SPRITE_HEALTHBAR_MARGIN * t + 0.5 * waypointsprite_fontsize;
582 SPRITE_HEALTHBAR_WIDTH * t,
583 SPRITE_HEALTHBAR_HEIGHT * t,
585 SPRITE_HEALTHBAR_BORDER * t,
588 a * SPRITE_HEALTHBAR_BORDERALPHA,
590 a * SPRITE_HEALTHBAR_HEALTHALPHA,
596 o = drawspritetext(o, ang, 0, rgb, a, waypointsprite_fontsize * '1 1 0', txt);
601 void Ent_RemoveWaypointSprite()
604 strunzone(self.netname);
606 strunzone(self.netname2);
608 strunzone(self.netname3);
611 void Ent_WaypointSprite()
614 sendflags = ReadByte();
617 self.spawntime = time;
619 self.draw2d = Draw_WaypointSprite;
621 InterpolateOrigin_Undo();
622 self.iflags |= IFLAG_ORIGIN;
629 self.health = t / 191.0;
630 self.build_finished = 0;
634 t = (t - 192) * 256 + ReadByte();
635 self.build_started = servertime;
636 if(self.build_finished)
637 self.build_starthealth = bound(0, self.health, 1);
639 self.build_starthealth = 0;
640 self.build_finished = servertime + t / 32;
646 self.build_finished = 0;
651 // unfortunately, this needs to be exact (for the 3D display)
652 self.origin_x = ReadCoord();
653 self.origin_y = ReadCoord();
654 self.origin_z = ReadCoord();
655 setorigin(self, self.origin);
660 self.team = ReadByte();
661 self.rule = ReadByte();
667 strunzone(self.netname);
668 self.netname = strzone(ReadString());
674 strunzone(self.netname2);
675 self.netname2 = strzone(ReadString());
681 strunzone(self.netname3);
682 self.netname3 = strzone(ReadString());
687 self.lifetime = ReadCoord();
688 self.fadetime = ReadCoord();
689 self.maxdistance = ReadShort();
690 self.hideflags = ReadByte();
696 self.teamradar_icon = (f & 0x7F);
699 self.(teamradar_times[self.teamradar_time_index]) = time;
700 self.teamradar_time_index = (self.teamradar_time_index + 1) % MAX_TEAMRADAR_TIMES;
702 self.teamradar_color_x = ReadByte() / 255.0;
703 self.teamradar_color_y = ReadByte() / 255.0;
704 self.teamradar_color_z = ReadByte() / 255.0;
705 self.helpme = ReadByte() * 0.1;
707 self.helpme += servertime;
710 InterpolateOrigin_Note();
712 self.entremove = Ent_RemoveWaypointSprite;
715 void WaypointSprite_Load_Frames(string ext)
717 float dh, n, i, o, f;
718 string s, sname, sframes;
719 dh = search_begin(strcat("models/sprites/*_frame*", ext), false, false);
722 float ext_len = strlen(ext);
723 n = search_getsize(dh);
724 for(i = 0; i < n; ++i)
726 s = search_getfilename(dh, i);
727 s = substring(s, 15, strlen(s) - 15 - ext_len); // strip models/sprites/ and extension
729 o = strstrofs(s, "_frame", 0);
730 sname = strcat("/spriteframes/", substring(s, 0, o));
731 sframes = substring(s, o + 6, strlen(s) - o - 6);
732 f = stof(sframes) + 1;
733 db_put(tempdb, sname, ftos(max(f, stof(db_get(tempdb, sname)))));
738 void WaypointSprite_Load()
740 waypointsprite_fadedistance = vlen(mi_scale);
741 waypointsprite_normdistance = autocvar_g_waypointsprite_normdistance;
742 waypointsprite_minscale = autocvar_g_waypointsprite_minscale;
743 waypointsprite_minalpha = autocvar_g_waypointsprite_minalpha;
744 waypointsprite_distancealphaexponent = autocvar_g_waypointsprite_distancealphaexponent;
745 waypointsprite_timealphaexponent = autocvar_g_waypointsprite_timealphaexponent;
746 waypointsprite_scale = autocvar_g_waypointsprite_scale;
747 waypointsprite_fontsize = autocvar_g_waypointsprite_fontsize;
748 waypointsprite_edgefadealpha = autocvar_g_waypointsprite_edgefadealpha;
749 waypointsprite_edgefadescale = autocvar_g_waypointsprite_edgefadescale;
750 waypointsprite_edgefadedistance = autocvar_g_waypointsprite_edgefadedistance;
751 waypointsprite_edgeoffset_bottom = autocvar_g_waypointsprite_edgeoffset_bottom;
752 waypointsprite_edgeoffset_left = autocvar_g_waypointsprite_edgeoffset_left;
753 waypointsprite_edgeoffset_right = autocvar_g_waypointsprite_edgeoffset_right;
754 waypointsprite_edgeoffset_top = autocvar_g_waypointsprite_edgeoffset_top;
755 waypointsprite_crosshairfadealpha = autocvar_g_waypointsprite_crosshairfadealpha;
756 waypointsprite_crosshairfadescale = autocvar_g_waypointsprite_crosshairfadescale;
757 waypointsprite_crosshairfadedistance = autocvar_g_waypointsprite_crosshairfadedistance;
758 waypointsprite_distancefadealpha = autocvar_g_waypointsprite_distancefadealpha;
759 waypointsprite_distancefadescale = autocvar_g_waypointsprite_distancefadescale;
760 waypointsprite_distancefadedistance = waypointsprite_fadedistance * autocvar_g_waypointsprite_distancefadedistancemultiplier;
761 waypointsprite_alpha = autocvar_g_waypointsprite_alpha * (1 - autocvar__menu_alpha);
763 if(!waypointsprite_initialized)
765 WaypointSprite_Load_Frames(".tga");
766 WaypointSprite_Load_Frames(".jpg");
767 waypointsprite_initialized = true;
770 waypointsprite_count = waypointsprite_newcount;
771 waypointsprite_newcount = 0;