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": return 2;
176 case "ons-cp-dfnd": return 0.5;
177 case "item_health_mega": return 2;
178 case "item_armor_large": return 2;
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-")
206 entity buff = BUFF_NULL;
207 FOREACH(BUFFS, it.m_sprite == s, LAMBDA(
211 return buff.m_prettyName;
216 case "as-push": return _("Push");
217 case "as-destroy": return _("Destroy");
218 case "as-defend": return _("Defend");
219 case "bluebase": return _("Blue base");
220 case "danger": return _("DANGER");
221 case "enemyflagcarrier": return _("Enemy carrier");
222 case "flagcarrier": return _("Flag carrier");
223 case "flagdropped": return _("Dropped flag");
224 case "helpme": return _("Help me!");
225 case "here": return _("Here");
226 case "key-dropped": return _("Dropped key");
227 case "keycarrier-blue": return _("Key carrier");
228 case "keycarrier-finish": return _("Run here");
229 case "keycarrier-friend": return _("Key carrier");
230 case "keycarrier-pink": return _("Key carrier");
231 case "keycarrier-red": return _("Key carrier");
232 case "keycarrier-yellow": return _("Key carrier");
233 case "redbase": return _("Red base");
234 case "yellowbase": return _("Yellow base");
235 case "neutralbase": return _("White base");
236 case "pinkbase": return _("Pink base");
237 case "waypoint": return _("Waypoint");
238 case "ons-gen": return _("Generator");
239 case "ons-gen-shielded": return _("Generator");
240 case "ons-cp": return _("Control point");
241 case "ons-cp-atck": return _("Control point");
242 case "ons-cp-dfnd": return _("Control point");
243 case "race-checkpoint": return _("Checkpoint");
244 case "race-finish": return _("Finish");
245 case "race-start": return _("Start");
246 case "race-start-finish": return (race_checkpointtime || race_mycheckpointtime) ? _("Finish") : _("Start");
247 case "goal": return _("Goal");
248 case "nb-ball": return _("Ball");
249 case "ka-ball": return _("Ball");
250 case "ka-ballcarrier": return _("Ball carrier");
251 case "dom-neut": return _("Control point");
252 case "dom-red": return _("Control point");
253 case "dom-blue": return _("Control point");
254 case "dom-yellow": return _("Control point");
255 case "dom-pink": return _("Control point");
256 case "item_health_mega": return _("Mega health");
257 case "item_armor_large": return _("Large armor");
258 case "item-invis": return _("Invisibility");
259 case "item-extralife": return _("Extra life");
260 case "item-speed": return _("Speed");
261 case "item-strength": return _("Strength");
262 case "item-shield": return _("Shield");
263 case "item-fuelregen": return _("Fuel regen");
264 case "item-jetpack": return _("Jet Pack");
265 case "frozen": return _("Frozen!");
266 case "tagged-target": return _("Tagged");
267 case "vehicle": return _("Vehicle");
272 vector fixrgbexcess_move(vector rgb, vector src, vector dst)
274 vector yvec = '0.299 0.587 0.114';
275 return rgb + dst * ((src * yvec) / (dst * yvec)) * ((rgb - '1 1 1') * src);
277 vector fixrgbexcess(vector rgb)
281 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 1 1');
284 rgb = fixrgbexcess_move(rgb, '0 1 0', '0 0 1');
290 rgb = fixrgbexcess_move(rgb, '0 0 1', '0 1 0');
297 rgb = fixrgbexcess_move(rgb, '0 1 0', '1 0 1');
300 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 0 1');
306 rgb = fixrgbexcess_move(rgb, '0 0 1', '1 0 0');
313 rgb = fixrgbexcess_move(rgb, '0 0 1', '1 1 0');
316 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 1 0');
322 rgb = fixrgbexcess_move(rgb, '0 1 0', '1 0 0');
330 void Draw_WaypointSprite()
336 self.alpha = pow(bound(0, (self.fadetime - time) / self.lifetime, 1), waypointsprite_timealphaexponent);
340 if(self.hideflags & 2)
341 return; // radar only
343 if(autocvar_cl_hidewaypoints >= 2)
346 if(self.hideflags & 1)
347 if(autocvar_cl_hidewaypoints)
348 return; // fixed waypoint
350 InterpolateOrigin_Do();
352 t = GetPlayerColor(player_localnum) + 1;
359 case SPRITERULE_SPECTATOR:
361 (autocvar_g_waypointsprite_itemstime == 1 && t == NUM_SPECTATOR + 1)
362 || (autocvar_g_waypointsprite_itemstime == 2 && (t == NUM_SPECTATOR + 1 || warmup_stage))
365 spriteimage = self.netname;
367 case SPRITERULE_DEFAULT:
371 spriteimage = self.netname;
376 spriteimage = self.netname;
378 case SPRITERULE_TEAMPLAY:
379 if(t == NUM_SPECTATOR + 1)
380 spriteimage = self.netname3;
381 else if(self.team == t)
382 spriteimage = self.netname2;
384 spriteimage = self.netname;
387 error("Invalid waypointsprite rule!");
391 if(spriteimage == "")
394 ++waypointsprite_newcount;
397 dist = vlen(self.origin - view_origin);
400 a = self.alpha * autocvar_hud_panel_fg_alpha;
402 if(self.maxdistance > waypointsprite_normdistance)
403 a *= pow(bound(0, (self.maxdistance - dist) / (self.maxdistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent);
404 else if(self.maxdistance > 0)
405 a *= pow(bound(0, (waypointsprite_fadedistance - dist) / (waypointsprite_fadedistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent) * (1 - waypointsprite_minalpha) + waypointsprite_minalpha;
408 rgb = self.teamradar_color;
409 rgb = spritelookupcolor(spriteimage, rgb);
412 self.teamradar_color = '1 0 1';
413 printf("WARNING: sprite of name %s has no color, using pink so you notice it\n", spriteimage);
416 if(time - floor(time) > 0.5)
418 if(self.helpme && time < self.helpme)
419 a *= SPRITE_HELPME_BLINK;
420 else if(!self.lifetime) // fading out waypoints don't blink
421 a *= spritelookupblinkvalue(spriteimage);
433 rgb = fixrgbexcess(rgb);
438 o = project_3d_to_2d(self.origin);
440 || o.x < (vid_conwidth * waypointsprite_edgeoffset_left)
441 || o.y < (vid_conheight * waypointsprite_edgeoffset_top)
442 || o.x > (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right))
443 || o.y > (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom)))
445 // scale it to be just in view
449 d = o - '0.5 0 0' * vid_conwidth - '0 0.5 0' * vid_conheight;
450 ang = atan2(-d.x, -d.y);
454 f1 = d.x / vid_conwidth;
455 f2 = d.y / vid_conheight;
457 if(max(f1, -f1) > max(f2, -f2))
462 d = d * ((0.5 - waypointsprite_edgeoffset_right) / f1);
467 d = d * (-(0.5 - waypointsprite_edgeoffset_left) / f1);
475 d = d * ((0.5 - waypointsprite_edgeoffset_bottom) / f2);
480 d = d * (-(0.5 - waypointsprite_edgeoffset_top) / f2);
484 o = d + '0.5 0 0' * vid_conwidth + '0 0.5 0' * vid_conheight;
492 d = o - '0.5 0 0' * vid_conwidth - '0 0.5 0' * vid_conheight;
493 ang = atan2(-d.x, -d.y);
498 float edgedistance_min, crosshairdistance;
499 edgedistance_min = min((o.y - (vid_conheight * waypointsprite_edgeoffset_top)),
500 (o.x - (vid_conwidth * waypointsprite_edgeoffset_left)),
501 (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right)) - o.x,
502 (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom)) - o.y);
505 vidscale = max(vid_conwidth / vid_width, vid_conheight / vid_height);
507 crosshairdistance = sqrt( pow(o.x - vid_conwidth/2, 2) + pow(o.y - vid_conheight/2, 2) );
509 t = waypointsprite_scale * vidscale;
510 a *= waypointsprite_alpha;
513 a = a * (1 - (1 - waypointsprite_distancefadealpha) * (bound(0, dist/waypointsprite_distancefadedistance, 1)));
514 t = t * (1 - (1 - waypointsprite_distancefadescale) * (bound(0, dist/waypointsprite_distancefadedistance, 1)));
516 if (edgedistance_min < waypointsprite_edgefadedistance) {
517 a = a * (1 - (1 - waypointsprite_edgefadealpha) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1)));
518 t = t * (1 - (1 - waypointsprite_edgefadescale) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1)));
520 if(crosshairdistance < waypointsprite_crosshairfadedistance) {
521 a = a * (1 - (1 - waypointsprite_crosshairfadealpha) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1)));
522 t = t * (1 - (1 - waypointsprite_crosshairfadescale) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1)));
525 if(self.build_finished)
527 if(time < self.build_finished + 0.25)
529 if(time < self.build_started)
530 self.health = self.build_starthealth;
531 else if(time < self.build_finished)
532 self.health = (time - self.build_started) / (self.build_finished - self.build_started) * (1 - self.build_starthealth) + self.build_starthealth;
540 o = drawspritearrow(o, ang, rgb, a, SPRITE_ARROW_SCALE * t);
543 if(autocvar_g_waypointsprite_spam && waypointsprite_count >= autocvar_g_waypointsprite_spam)
546 txt = spritelookuptext(spriteimage);
547 if(self.helpme && time < self.helpme)
548 txt = sprintf(_("%s needing help!"), txt);
549 if(autocvar_g_waypointsprite_uppercase)
550 txt = strtoupper(txt);
552 draw_beginBoldFont();
555 o = drawspritetext(o, ang, (SPRITE_HEALTHBAR_WIDTH + 2 * SPRITE_HEALTHBAR_BORDER) * t, rgb, a, waypointsprite_fontsize * '1 1 0', txt);
558 if(self.build_finished)
563 marg = -(SPRITE_HEALTHBAR_MARGIN + SPRITE_HEALTHBAR_HEIGHT + 2 * SPRITE_HEALTHBAR_BORDER) * t - 0.5 * waypointsprite_fontsize;
565 marg = SPRITE_HEALTHBAR_MARGIN * t + 0.5 * waypointsprite_fontsize;
572 SPRITE_HEALTHBAR_WIDTH * t,
573 SPRITE_HEALTHBAR_HEIGHT * t,
575 SPRITE_HEALTHBAR_BORDER * t,
578 a * SPRITE_HEALTHBAR_BORDERALPHA,
580 a * SPRITE_HEALTHBAR_HEALTHALPHA,
586 o = drawspritetext(o, ang, 0, rgb, a, waypointsprite_fontsize * '1 1 0', txt);
591 void Ent_RemoveWaypointSprite()
594 strunzone(self.netname);
596 strunzone(self.netname2);
598 strunzone(self.netname3);
601 void Ent_WaypointSprite()
604 sendflags = ReadByte();
607 self.spawntime = time;
609 self.draw2d = Draw_WaypointSprite;
611 InterpolateOrigin_Undo();
612 self.iflags |= IFLAG_ORIGIN;
619 self.health = t / 191.0;
620 self.build_finished = 0;
624 t = (t - 192) * 256 + ReadByte();
625 self.build_started = servertime;
626 if(self.build_finished)
627 self.build_starthealth = bound(0, self.health, 1);
629 self.build_starthealth = 0;
630 self.build_finished = servertime + t / 32;
636 self.build_finished = 0;
641 // unfortunately, this needs to be exact (for the 3D display)
642 self.origin_x = ReadCoord();
643 self.origin_y = ReadCoord();
644 self.origin_z = ReadCoord();
645 setorigin(self, self.origin);
650 self.team = ReadByte();
651 self.rule = ReadByte();
657 strunzone(self.netname);
658 self.netname = strzone(ReadString());
664 strunzone(self.netname2);
665 self.netname2 = strzone(ReadString());
671 strunzone(self.netname3);
672 self.netname3 = strzone(ReadString());
677 self.lifetime = ReadCoord();
678 self.fadetime = ReadCoord();
679 self.maxdistance = ReadShort();
680 self.hideflags = ReadByte();
686 self.teamradar_icon = (f & 0x7F);
689 self.(teamradar_times[self.teamradar_time_index]) = time;
690 self.teamradar_time_index = (self.teamradar_time_index + 1) % MAX_TEAMRADAR_TIMES;
692 self.teamradar_color_x = ReadByte() / 255.0;
693 self.teamradar_color_y = ReadByte() / 255.0;
694 self.teamradar_color_z = ReadByte() / 255.0;
695 self.helpme = ReadByte() * 0.1;
697 self.helpme += servertime;
700 InterpolateOrigin_Note();
702 self.entremove = Ent_RemoveWaypointSprite;
705 void WaypointSprite_Load_Frames(string ext)
707 float dh, n, i, o, f;
708 string s, sname, sframes;
709 dh = search_begin(strcat("models/sprites/*_frame*", ext), false, false);
712 float ext_len = strlen(ext);
713 n = search_getsize(dh);
714 for(i = 0; i < n; ++i)
716 s = search_getfilename(dh, i);
717 s = substring(s, 15, strlen(s) - 15 - ext_len); // strip models/sprites/ and extension
719 o = strstrofs(s, "_frame", 0);
720 sname = strcat("/spriteframes/", substring(s, 0, o));
721 sframes = substring(s, o + 6, strlen(s) - o - 6);
722 f = stof(sframes) + 1;
723 db_put(tempdb, sname, ftos(max(f, stof(db_get(tempdb, sname)))));
728 void WaypointSprite_Load()
730 waypointsprite_fadedistance = vlen(mi_scale);
731 waypointsprite_normdistance = autocvar_g_waypointsprite_normdistance;
732 waypointsprite_minscale = autocvar_g_waypointsprite_minscale;
733 waypointsprite_minalpha = autocvar_g_waypointsprite_minalpha;
734 waypointsprite_distancealphaexponent = autocvar_g_waypointsprite_distancealphaexponent;
735 waypointsprite_timealphaexponent = autocvar_g_waypointsprite_timealphaexponent;
736 waypointsprite_scale = autocvar_g_waypointsprite_scale;
737 waypointsprite_fontsize = autocvar_g_waypointsprite_fontsize;
738 waypointsprite_edgefadealpha = autocvar_g_waypointsprite_edgefadealpha;
739 waypointsprite_edgefadescale = autocvar_g_waypointsprite_edgefadescale;
740 waypointsprite_edgefadedistance = autocvar_g_waypointsprite_edgefadedistance;
741 waypointsprite_edgeoffset_bottom = autocvar_g_waypointsprite_edgeoffset_bottom;
742 waypointsprite_edgeoffset_left = autocvar_g_waypointsprite_edgeoffset_left;
743 waypointsprite_edgeoffset_right = autocvar_g_waypointsprite_edgeoffset_right;
744 waypointsprite_edgeoffset_top = autocvar_g_waypointsprite_edgeoffset_top;
745 waypointsprite_crosshairfadealpha = autocvar_g_waypointsprite_crosshairfadealpha;
746 waypointsprite_crosshairfadescale = autocvar_g_waypointsprite_crosshairfadescale;
747 waypointsprite_crosshairfadedistance = autocvar_g_waypointsprite_crosshairfadedistance;
748 waypointsprite_distancefadealpha = autocvar_g_waypointsprite_distancefadealpha;
749 waypointsprite_distancefadescale = autocvar_g_waypointsprite_distancefadescale;
750 waypointsprite_distancefadedistance = waypointsprite_fadedistance * autocvar_g_waypointsprite_distancefadedistancemultiplier;
751 waypointsprite_alpha = autocvar_g_waypointsprite_alpha * (1 - autocvar__menu_alpha);
753 if(!waypointsprite_initialized)
755 WaypointSprite_Load_Frames(".tga");
756 WaypointSprite_Load_Frames(".jpg");
757 waypointsprite_initialized = true;
760 waypointsprite_count = waypointsprite_newcount;
761 waypointsprite_newcount = 0;