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_health_mega": return 2;
177 case "item_armor_large": return 2;
178 case "item-invis": return 2;
179 case "item-extralife": return 2;
180 case "item-speed": return 2;
181 case "item-strength": return 2;
182 case "item-shield": return 2;
183 case "item-fuelregen": return 2;
184 case "item-jetpack": return 2;
185 case "wpn-fireball": return 2; // superweapon
186 case "wpn-minstanex": return 2; // superweapon
187 case "wpn-porto": return 2; // superweapon
188 case "tagged-target": return 2;
192 vector spritelookupcolor(string s, vector def)
194 if(substring(s, 0, 4) == "wpn-")
195 return (get_weaponinfo(stof(substring(s, 4, strlen(s)))).wpcolor);
199 case "keycarrier-friend": return '0 1 0';
203 string spritelookuptext(string s)
205 if(substring(s, 0, 4) == "wpn-") { return (get_weaponinfo(stof(substring(s, 4, strlen(s)))).message); }
206 if(substring(s, 0, 5) == "buff-") { return Buff_PrettyName(Buff_Type_FromSprite(s)); }
210 case "as-push": return _("Push");
211 case "as-destroy": return _("Destroy");
212 case "as-defend": return _("Defend");
213 case "bluebase": return _("Blue base");
214 case "danger": return _("DANGER");
215 case "enemyflagcarrier": return _("Enemy carrier");
216 case "flagcarrier": return _("Flag carrier");
217 case "flagdropped": return _("Dropped flag");
218 case "helpme": return _("Help me!");
219 case "here": return _("Here");
220 case "key-dropped": return _("Dropped key");
221 case "keycarrier-blue": return _("Key carrier");
222 case "keycarrier-finish": return _("Run here");
223 case "keycarrier-friend": return _("Key carrier");
224 case "keycarrier-pink": return _("Key carrier");
225 case "keycarrier-red": return _("Key carrier");
226 case "keycarrier-yellow": return _("Key carrier");
227 case "redbase": return _("Red base");
228 case "waypoint": return _("Waypoint");
229 case "ons-gen-red": return _("Generator");
230 case "ons-gen-blue": return _("Generator");
231 case "ons-gen-shielded": return _("Generator");
232 case "ons-cp-neut": return _("Control point");
233 case "ons-cp-red": return _("Control point");
234 case "ons-cp-blue": return _("Control point");
235 case "ons-cp-atck-neut": return _("Control point");
236 case "ons-cp-atck-red": return _("Control point");
237 case "ons-cp-atck-blue": return _("Control point");
238 case "ons-cp-dfnd-red": return _("Control point");
239 case "ons-cp-dfnd-blue": return _("Control point");
240 case "race-checkpoint": return _("Checkpoint");
241 case "race-finish": return _("Finish");
242 case "race-start": return _("Start");
243 case "race-start-finish": return (race_checkpointtime || race_mycheckpointtime) ? _("Finish") : _("Start");
244 case "goal": return _("Goal");
245 case "nb-ball": return _("Ball");
246 case "ka-ball": return _("Ball");
247 case "ka-ballcarrier": return _("Ball carrier");
248 case "dom-neut": return _("Control point");
249 case "dom-red": return _("Control point");
250 case "dom-blue": return _("Control point");
251 case "dom-yellow": return _("Control point");
252 case "dom-pink": return _("Control point");
253 case "item_health_mega": return _("Mega health");
254 case "item_armor_large": return _("Large armor");
255 case "item-invis": return _("Invisibility");
256 case "item-extralife": return _("Extra life");
257 case "item-speed": return _("Speed");
258 case "item-strength": return _("Strength");
259 case "item-shield": return _("Shield");
260 case "item-fuelregen": return _("Fuel regen");
261 case "item-jetpack": return _("Jet Pack");
262 case "frozen": return _("Frozen!");
263 case "tagged-target": return _("Tagged");
264 case "vehicle": return _("Vehicle");
269 vector fixrgbexcess_move(vector rgb, vector src, vector dst)
271 vector yvec = '0.299 0.587 0.114';
272 return rgb + dst * ((src * yvec) / (dst * yvec)) * ((rgb - '1 1 1') * src);
274 vector fixrgbexcess(vector rgb)
278 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 1 1');
281 rgb = fixrgbexcess_move(rgb, '0 1 0', '0 0 1');
287 rgb = fixrgbexcess_move(rgb, '0 0 1', '0 1 0');
294 rgb = fixrgbexcess_move(rgb, '0 1 0', '1 0 1');
297 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 0 1');
303 rgb = fixrgbexcess_move(rgb, '0 0 1', '1 0 0');
310 rgb = fixrgbexcess_move(rgb, '0 0 1', '1 1 0');
313 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 1 0');
319 rgb = fixrgbexcess_move(rgb, '0 1 0', '1 0 0');
327 void Draw_WaypointSprite()
333 self.alpha = pow(bound(0, (self.fadetime - time) / self.lifetime, 1), waypointsprite_timealphaexponent);
337 if(self.hideflags & 2)
338 return; // radar only
340 if(autocvar_cl_hidewaypoints >= 2)
343 if(self.hideflags & 1)
344 if(autocvar_cl_hidewaypoints)
345 return; // fixed waypoint
347 InterpolateOrigin_Do();
349 t = GetPlayerColor(player_localnum) + 1;
356 case SPRITERULE_SPECTATOR:
358 (autocvar_g_waypointsprite_itemstime == 1 && t == NUM_SPECTATOR + 1)
359 || (autocvar_g_waypointsprite_itemstime == 2 && (t == NUM_SPECTATOR + 1 || warmup_stage))
362 spriteimage = self.netname;
364 case SPRITERULE_DEFAULT:
368 spriteimage = self.netname;
373 spriteimage = self.netname;
375 case SPRITERULE_TEAMPLAY:
376 if(t == NUM_SPECTATOR + 1)
377 spriteimage = self.netname3;
378 else if(self.team == t)
379 spriteimage = self.netname2;
381 spriteimage = self.netname;
384 error("Invalid waypointsprite rule!");
388 if(spriteimage == "")
391 ++waypointsprite_newcount;
394 dist = vlen(self.origin - view_origin);
397 a = self.alpha * autocvar_hud_panel_fg_alpha;
399 if(self.maxdistance > waypointsprite_normdistance)
400 a *= pow(bound(0, (self.maxdistance - dist) / (self.maxdistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent);
401 else if(self.maxdistance > 0)
402 a *= pow(bound(0, (waypointsprite_fadedistance - dist) / (waypointsprite_fadedistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent) * (1 - waypointsprite_minalpha) + waypointsprite_minalpha;
405 rgb = self.teamradar_color;
406 rgb = spritelookupcolor(spriteimage, rgb);
409 self.teamradar_color = '1 0 1';
410 printf("WARNING: sprite of name %s has no color, using pink so you notice it\n", spriteimage);
413 if(time - floor(time) > 0.5)
415 if(self.helpme && time < self.helpme)
416 a *= SPRITE_HELPME_BLINK;
418 a *= spritelookupblinkvalue(spriteimage);
430 rgb = fixrgbexcess(rgb);
435 o = project_3d_to_2d(self.origin);
437 || o.x < (vid_conwidth * waypointsprite_edgeoffset_left)
438 || o.y < (vid_conheight * waypointsprite_edgeoffset_top)
439 || o.x > (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right))
440 || o.y > (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom)))
442 // scale it to be just in view
446 d = o - '0.5 0 0' * vid_conwidth - '0 0.5 0' * vid_conheight;
447 ang = atan2(-d.x, -d.y);
451 f1 = d.x / vid_conwidth;
452 f2 = d.y / vid_conheight;
454 if(max(f1, -f1) > max(f2, -f2))
459 d = d * ((0.5 - waypointsprite_edgeoffset_right) / f1);
464 d = d * (-(0.5 - waypointsprite_edgeoffset_left) / f1);
472 d = d * ((0.5 - waypointsprite_edgeoffset_bottom) / f2);
477 d = d * (-(0.5 - waypointsprite_edgeoffset_top) / f2);
481 o = d + '0.5 0 0' * vid_conwidth + '0 0.5 0' * vid_conheight;
489 d = o - '0.5 0 0' * vid_conwidth - '0 0.5 0' * vid_conheight;
490 ang = atan2(-d.x, -d.y);
495 float edgedistance_min, crosshairdistance;
496 edgedistance_min = min((o.y - (vid_conheight * waypointsprite_edgeoffset_top)),
497 (o.x - (vid_conwidth * waypointsprite_edgeoffset_left)),
498 (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right)) - o.x,
499 (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom)) - o.y);
502 vidscale = max(vid_conwidth / vid_width, vid_conheight / vid_height);
504 crosshairdistance = sqrt( pow(o.x - vid_conwidth/2, 2) + pow(o.y - vid_conheight/2, 2) );
506 t = waypointsprite_scale * vidscale;
507 a *= waypointsprite_alpha;
510 a = a * (1 - (1 - waypointsprite_distancefadealpha) * (bound(0, dist/waypointsprite_distancefadedistance, 1)));
511 t = t * (1 - (1 - waypointsprite_distancefadescale) * (bound(0, dist/waypointsprite_distancefadedistance, 1)));
513 if (edgedistance_min < waypointsprite_edgefadedistance) {
514 a = a * (1 - (1 - waypointsprite_edgefadealpha) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1)));
515 t = t * (1 - (1 - waypointsprite_edgefadescale) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1)));
517 if(crosshairdistance < waypointsprite_crosshairfadedistance) {
518 a = a * (1 - (1 - waypointsprite_crosshairfadealpha) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1)));
519 t = t * (1 - (1 - waypointsprite_crosshairfadescale) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1)));
522 if(self.build_finished)
524 if(time < self.build_finished + 0.25)
526 if(time < self.build_started)
527 self.health = self.build_starthealth;
528 else if(time < self.build_finished)
529 self.health = (time - self.build_started) / (self.build_finished - self.build_started) * (1 - self.build_starthealth) + self.build_starthealth;
537 o = drawspritearrow(o, ang, rgb, a, SPRITE_ARROW_SCALE * t);
540 if(autocvar_g_waypointsprite_spam && waypointsprite_count >= autocvar_g_waypointsprite_spam)
543 txt = spritelookuptext(spriteimage);
544 if(self.helpme && time < self.helpme)
545 txt = sprintf(_("%s needing help!"), txt);
546 if(autocvar_g_waypointsprite_uppercase)
547 txt = strtoupper(txt);
549 draw_beginBoldFont();
552 o = drawspritetext(o, ang, (SPRITE_HEALTHBAR_WIDTH + 2 * SPRITE_HEALTHBAR_BORDER) * t, rgb, a, waypointsprite_fontsize * '1 1 0', txt);
555 if(self.build_finished)
560 marg = -(SPRITE_HEALTHBAR_MARGIN + SPRITE_HEALTHBAR_HEIGHT + 2 * SPRITE_HEALTHBAR_BORDER) * t - 0.5 * waypointsprite_fontsize;
562 marg = SPRITE_HEALTHBAR_MARGIN * t + 0.5 * waypointsprite_fontsize;
569 SPRITE_HEALTHBAR_WIDTH * t,
570 SPRITE_HEALTHBAR_HEIGHT * t,
572 SPRITE_HEALTHBAR_BORDER * t,
575 a * SPRITE_HEALTHBAR_BORDERALPHA,
577 a * SPRITE_HEALTHBAR_HEALTHALPHA,
583 o = drawspritetext(o, ang, 0, rgb, a, waypointsprite_fontsize * '1 1 0', txt);
588 void Ent_RemoveWaypointSprite()
591 strunzone(self.netname);
593 strunzone(self.netname2);
595 strunzone(self.netname3);
598 void Ent_WaypointSprite()
601 sendflags = ReadByte();
604 self.spawntime = time;
606 self.draw2d = Draw_WaypointSprite;
608 InterpolateOrigin_Undo();
609 self.iflags |= IFLAG_ORIGIN;
616 self.health = t / 191.0;
617 self.build_finished = 0;
621 t = (t - 192) * 256 + ReadByte();
622 self.build_started = servertime;
623 if(self.build_finished)
624 self.build_starthealth = bound(0, self.health, 1);
626 self.build_starthealth = 0;
627 self.build_finished = servertime + t / 32;
633 self.build_finished = 0;
638 // unfortunately, this needs to be exact (for the 3D display)
639 self.origin_x = ReadCoord();
640 self.origin_y = ReadCoord();
641 self.origin_z = ReadCoord();
642 setorigin(self, self.origin);
647 self.team = ReadByte();
648 self.rule = ReadByte();
654 strunzone(self.netname);
655 self.netname = strzone(ReadString());
661 strunzone(self.netname2);
662 self.netname2 = strzone(ReadString());
668 strunzone(self.netname3);
669 self.netname3 = strzone(ReadString());
674 self.lifetime = ReadCoord();
675 self.fadetime = ReadCoord();
676 self.maxdistance = ReadShort();
677 self.hideflags = ReadByte();
683 self.teamradar_icon = (f & 0x7F);
686 self.(teamradar_times[self.teamradar_time_index]) = time;
687 self.teamradar_time_index = (self.teamradar_time_index + 1) % MAX_TEAMRADAR_TIMES;
689 self.teamradar_color_x = ReadByte() / 255.0;
690 self.teamradar_color_y = ReadByte() / 255.0;
691 self.teamradar_color_z = ReadByte() / 255.0;
692 self.helpme = ReadByte() * 0.1;
694 self.helpme += servertime;
697 InterpolateOrigin_Note();
699 self.entremove = Ent_RemoveWaypointSprite;
702 void WaypointSprite_Load_Frames(string ext)
704 float dh, n, i, o, f;
705 string s, sname, sframes;
706 dh = search_begin(strcat("models/sprites/*_frame*", ext), false, false);
709 float ext_len = strlen(ext);
710 n = search_getsize(dh);
711 for(i = 0; i < n; ++i)
713 s = search_getfilename(dh, i);
714 s = substring(s, 15, strlen(s) - 15 - ext_len); // strip models/sprites/ and extension
716 o = strstrofs(s, "_frame", 0);
717 sname = strcat("/spriteframes/", substring(s, 0, o));
718 sframes = substring(s, o + 6, strlen(s) - o - 6);
719 f = stof(sframes) + 1;
720 db_put(tempdb, sname, ftos(max(f, stof(db_get(tempdb, sname)))));
725 void WaypointSprite_Load()
727 waypointsprite_fadedistance = vlen(mi_scale);
728 waypointsprite_normdistance = autocvar_g_waypointsprite_normdistance;
729 waypointsprite_minscale = autocvar_g_waypointsprite_minscale;
730 waypointsprite_minalpha = autocvar_g_waypointsprite_minalpha;
731 waypointsprite_distancealphaexponent = autocvar_g_waypointsprite_distancealphaexponent;
732 waypointsprite_timealphaexponent = autocvar_g_waypointsprite_timealphaexponent;
733 waypointsprite_scale = autocvar_g_waypointsprite_scale;
734 waypointsprite_fontsize = autocvar_g_waypointsprite_fontsize;
735 waypointsprite_edgefadealpha = autocvar_g_waypointsprite_edgefadealpha;
736 waypointsprite_edgefadescale = autocvar_g_waypointsprite_edgefadescale;
737 waypointsprite_edgefadedistance = autocvar_g_waypointsprite_edgefadedistance;
738 waypointsprite_edgeoffset_bottom = autocvar_g_waypointsprite_edgeoffset_bottom;
739 waypointsprite_edgeoffset_left = autocvar_g_waypointsprite_edgeoffset_left;
740 waypointsprite_edgeoffset_right = autocvar_g_waypointsprite_edgeoffset_right;
741 waypointsprite_edgeoffset_top = autocvar_g_waypointsprite_edgeoffset_top;
742 waypointsprite_crosshairfadealpha = autocvar_g_waypointsprite_crosshairfadealpha;
743 waypointsprite_crosshairfadescale = autocvar_g_waypointsprite_crosshairfadescale;
744 waypointsprite_crosshairfadedistance = autocvar_g_waypointsprite_crosshairfadedistance;
745 waypointsprite_distancefadealpha = autocvar_g_waypointsprite_distancefadealpha;
746 waypointsprite_distancefadescale = autocvar_g_waypointsprite_distancefadescale;
747 waypointsprite_distancefadedistance = waypointsprite_fadedistance * autocvar_g_waypointsprite_distancefadedistancemultiplier;
748 waypointsprite_alpha = autocvar_g_waypointsprite_alpha * (1 - autocvar__menu_alpha);
750 if(!waypointsprite_initialized)
752 WaypointSprite_Load_Frames(".tga");
753 WaypointSprite_Load_Frames(".jpg");
754 waypointsprite_initialized = true;
757 waypointsprite_count = waypointsprite_newcount;
758 waypointsprite_newcount = 0;