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": return 2;
172 case "ons-cp-dfnd": return 0.5;
173 case "item-invis": return 2;
174 case "item-extralife": return 2;
175 case "item-speed": return 2;
176 case "item-strength": return 2;
177 case "item-shield": return 2;
178 case "item-fuelregen": return 2;
179 case "item-jetpack": return 2;
180 case "tagged-target": return 2;
184 vector spritelookupcolor(string s, vector def)
186 if(substring(s, 0, 4) == "wpn-")
187 return (get_weaponinfo(stof(substring(s, 4, strlen(s)))).wpcolor);
191 case "keycarrier-friend": return '0 1 0';
195 string spritelookuptext(string s)
197 if(substring(s, 0, 4) == "wpn-") { return (get_weaponinfo(stof(substring(s, 4, strlen(s)))).message); }
198 if(substring(s, 0, 5) == "buff-") { return Buff_PrettyName(Buff_Type_FromSprite(s)); }
202 case "as-push": return _("Push");
203 case "as-destroy": return _("Destroy");
204 case "as-defend": return _("Defend");
205 case "bluebase": return _("Blue base");
206 case "danger": return _("DANGER");
207 case "enemyflagcarrier": return _("Enemy carrier");
208 case "flagcarrier": return _("Flag carrier");
209 case "flagdropped": return _("Dropped flag");
210 case "helpme": return _("Help me!");
211 case "here": return _("Here");
212 case "key-dropped": return _("Dropped key");
213 case "keycarrier-blue": return _("Key carrier");
214 case "keycarrier-finish": return _("Run here");
215 case "keycarrier-friend": return _("Key carrier");
216 case "keycarrier-pink": return _("Key carrier");
217 case "keycarrier-red": return _("Key carrier");
218 case "keycarrier-yellow": return _("Key carrier");
219 case "redbase": return _("Red base");
220 case "waypoint": return _("Waypoint");
221 case "ons-gen": return _("Generator");
222 case "ons-gen-shielded": return _("Generator");
223 case "ons-cp": return _("Control point");
224 case "ons-cp-atck": return _("Control point");
225 case "ons-cp-dfnd": return _("Control point");
226 case "race-checkpoint": return _("Checkpoint");
227 case "race-finish": return _("Finish");
228 case "race-start": return _("Start");
229 case "race-start-finish": return (race_checkpointtime || race_mycheckpointtime) ? _("Finish") : _("Start");
230 case "goal": return _("Goal");
231 case "nb-ball": return _("Ball");
232 case "ka-ball": return _("Ball");
233 case "ka-ballcarrier": return _("Ball carrier");
234 case "dom-neut": return _("Control point");
235 case "dom-red": return _("Control point");
236 case "dom-blue": return _("Control point");
237 case "dom-yellow": return _("Control point");
238 case "dom-pink": return _("Control point");
239 case "item-invis": return _("Invisibility");
240 case "item-extralife": return _("Extra life");
241 case "item-speed": return _("Speed");
242 case "item-strength": return _("Strength");
243 case "item-shield": return _("Shield");
244 case "item-fuelregen": return _("Fuel regen");
245 case "item-jetpack": return _("Jet Pack");
246 case "frozen": return _("Frozen!");
247 case "tagged-target": return _("Tagged");
248 case "vehicle": return _("Vehicle");
253 vector fixrgbexcess_move(vector rgb, vector src, vector dst)
255 vector yvec = '0.299 0.587 0.114';
256 return rgb + dst * ((src * yvec) / (dst * yvec)) * ((rgb - '1 1 1') * src);
258 vector fixrgbexcess(vector rgb)
262 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 1 1');
265 rgb = fixrgbexcess_move(rgb, '0 1 0', '0 0 1');
271 rgb = fixrgbexcess_move(rgb, '0 0 1', '0 1 0');
278 rgb = fixrgbexcess_move(rgb, '0 1 0', '1 0 1');
281 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 0 1');
287 rgb = fixrgbexcess_move(rgb, '0 0 1', '1 0 0');
294 rgb = fixrgbexcess_move(rgb, '0 0 1', '1 1 0');
297 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 1 0');
303 rgb = fixrgbexcess_move(rgb, '0 1 0', '1 0 0');
311 void Draw_WaypointSprite()
317 self.alpha = pow(bound(0, (self.fadetime - time) / self.lifetime, 1), waypointsprite_timealphaexponent);
321 if(self.hideflags & 2)
322 return; // radar only
324 if(autocvar_cl_hidewaypoints >= 2)
327 if(self.hideflags & 1)
328 if(autocvar_cl_hidewaypoints)
329 return; // fixed waypoint
331 InterpolateOrigin_Do();
333 t = GetPlayerColor(player_localnum) + 1;
340 case SPRITERULE_DEFAULT:
344 spriteimage = self.netname;
349 spriteimage = self.netname;
351 case SPRITERULE_TEAMPLAY:
352 if(t == NUM_SPECTATOR + 1)
353 spriteimage = self.netname3;
354 else if(self.team == t)
355 spriteimage = self.netname2;
357 spriteimage = self.netname;
360 error("Invalid waypointsprite rule!");
364 if(spriteimage == "")
367 ++waypointsprite_newcount;
370 dist = vlen(self.origin - view_origin);
373 a = self.alpha * autocvar_hud_panel_fg_alpha;
375 if(self.maxdistance > waypointsprite_normdistance)
376 a *= pow(bound(0, (self.maxdistance - dist) / (self.maxdistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent);
377 else if(self.maxdistance > 0)
378 a *= pow(bound(0, (waypointsprite_fadedistance - dist) / (waypointsprite_fadedistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent) * (1 - waypointsprite_minalpha) + waypointsprite_minalpha;
381 rgb = self.teamradar_color;
382 rgb = spritelookupcolor(spriteimage, rgb);
385 self.teamradar_color = '1 0 1';
386 printf("WARNING: sprite of name %s has no color, using pink so you notice it\n", spriteimage);
389 if(time - floor(time) > 0.5)
391 if(self.helpme && time < self.helpme)
392 a *= SPRITE_HELPME_BLINK;
394 a *= spritelookupblinkvalue(spriteimage);
406 rgb = fixrgbexcess(rgb);
411 o = project_3d_to_2d(self.origin);
413 || o.x < (vid_conwidth * waypointsprite_edgeoffset_left)
414 || o.y < (vid_conheight * waypointsprite_edgeoffset_top)
415 || o.x > (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right))
416 || o.y > (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom)))
418 // scale it to be just in view
422 d = o - '0.5 0 0' * vid_conwidth - '0 0.5 0' * vid_conheight;
423 ang = atan2(-d.x, -d.y);
427 f1 = d.x / vid_conwidth;
428 f2 = d.y / vid_conheight;
430 if(max(f1, -f1) > max(f2, -f2))
435 d = d * ((0.5 - waypointsprite_edgeoffset_right) / f1);
440 d = d * (-(0.5 - waypointsprite_edgeoffset_left) / f1);
448 d = d * ((0.5 - waypointsprite_edgeoffset_bottom) / f2);
453 d = d * (-(0.5 - waypointsprite_edgeoffset_top) / f2);
457 o = d + '0.5 0 0' * vid_conwidth + '0 0.5 0' * vid_conheight;
465 d = o - '0.5 0 0' * vid_conwidth - '0 0.5 0' * vid_conheight;
466 ang = atan2(-d.x, -d.y);
471 float edgedistance_min, crosshairdistance;
472 edgedistance_min = min((o.y - (vid_conheight * waypointsprite_edgeoffset_top)),
473 (o.x - (vid_conwidth * waypointsprite_edgeoffset_left)),
474 (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right)) - o.x,
475 (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom)) - o.y);
478 vidscale = max(vid_conwidth / vid_width, vid_conheight / vid_height);
480 crosshairdistance = sqrt( pow(o.x - vid_conwidth/2, 2) + pow(o.y - vid_conheight/2, 2) );
482 t = waypointsprite_scale * vidscale;
483 a *= waypointsprite_alpha;
486 a = a * (1 - (1 - waypointsprite_distancefadealpha) * (bound(0, dist/waypointsprite_distancefadedistance, 1)));
487 t = t * (1 - (1 - waypointsprite_distancefadescale) * (bound(0, dist/waypointsprite_distancefadedistance, 1)));
489 if (edgedistance_min < waypointsprite_edgefadedistance) {
490 a = a * (1 - (1 - waypointsprite_edgefadealpha) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1)));
491 t = t * (1 - (1 - waypointsprite_edgefadescale) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1)));
493 if(crosshairdistance < waypointsprite_crosshairfadedistance) {
494 a = a * (1 - (1 - waypointsprite_crosshairfadealpha) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1)));
495 t = t * (1 - (1 - waypointsprite_crosshairfadescale) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1)));
498 if(self.build_finished)
500 if(time < self.build_finished + 0.25)
502 if(time < self.build_started)
503 self.health = self.build_starthealth;
504 else if(time < self.build_finished)
505 self.health = (time - self.build_started) / (self.build_finished - self.build_started) * (1 - self.build_starthealth) + self.build_starthealth;
513 o = drawspritearrow(o, ang, rgb, a, SPRITE_ARROW_SCALE * t);
516 if(autocvar_g_waypointsprite_spam && waypointsprite_count >= autocvar_g_waypointsprite_spam)
519 txt = spritelookuptext(spriteimage);
520 if(self.helpme && time < self.helpme)
521 txt = sprintf(_("%s needing help!"), txt);
522 if(autocvar_g_waypointsprite_uppercase)
523 txt = strtoupper(txt);
525 draw_beginBoldFont();
528 o = drawspritetext(o, ang, (SPRITE_HEALTHBAR_WIDTH + 2 * SPRITE_HEALTHBAR_BORDER) * t, rgb, a, waypointsprite_fontsize * '1 1 0', txt);
531 if(self.build_finished)
536 marg = -(SPRITE_HEALTHBAR_MARGIN + SPRITE_HEALTHBAR_HEIGHT + 2 * SPRITE_HEALTHBAR_BORDER) * t - 0.5 * waypointsprite_fontsize;
538 marg = SPRITE_HEALTHBAR_MARGIN * t + 0.5 * waypointsprite_fontsize;
545 SPRITE_HEALTHBAR_WIDTH * t,
546 SPRITE_HEALTHBAR_HEIGHT * t,
548 SPRITE_HEALTHBAR_BORDER * t,
551 a * SPRITE_HEALTHBAR_BORDERALPHA,
553 a * SPRITE_HEALTHBAR_HEALTHALPHA,
559 o = drawspritetext(o, ang, 0, rgb, a, waypointsprite_fontsize * '1 1 0', txt);
564 void Ent_RemoveWaypointSprite()
567 strunzone(self.netname);
569 strunzone(self.netname2);
571 strunzone(self.netname3);
574 void Ent_WaypointSprite()
577 sendflags = ReadByte();
580 self.spawntime = time;
582 self.draw2d = Draw_WaypointSprite;
584 InterpolateOrigin_Undo();
585 self.iflags |= IFLAG_ORIGIN;
592 self.health = t / 191.0;
593 self.build_finished = 0;
597 t = (t - 192) * 256 + ReadByte();
598 self.build_started = servertime;
599 if(self.build_finished)
600 self.build_starthealth = bound(0, self.health, 1);
602 self.build_starthealth = 0;
603 self.build_finished = servertime + t / 32;
609 self.build_finished = 0;
614 // unfortunately, this needs to be exact (for the 3D display)
615 self.origin_x = ReadCoord();
616 self.origin_y = ReadCoord();
617 self.origin_z = ReadCoord();
618 setorigin(self, self.origin);
623 self.team = ReadByte();
624 self.rule = ReadByte();
630 strunzone(self.netname);
631 self.netname = strzone(ReadString());
637 strunzone(self.netname2);
638 self.netname2 = strzone(ReadString());
644 strunzone(self.netname3);
645 self.netname3 = strzone(ReadString());
650 self.lifetime = ReadCoord();
651 self.fadetime = ReadCoord();
652 self.maxdistance = ReadShort();
653 self.hideflags = ReadByte();
659 self.teamradar_icon = (f & 0x7F);
662 self.(teamradar_times[self.teamradar_time_index]) = time;
663 self.teamradar_time_index = (self.teamradar_time_index + 1) % MAX_TEAMRADAR_TIMES;
665 self.teamradar_color_x = ReadByte() / 255.0;
666 self.teamradar_color_y = ReadByte() / 255.0;
667 self.teamradar_color_z = ReadByte() / 255.0;
668 self.helpme = ReadByte() * 0.1;
670 self.helpme += servertime;
673 InterpolateOrigin_Note();
675 self.entremove = Ent_RemoveWaypointSprite;
678 void WaypointSprite_Load_Frames(string ext)
680 float dh, n, i, o, f;
681 string s, sname, sframes;
682 dh = search_begin(strcat("models/sprites/*_frame*", ext), false, false);
685 float ext_len = strlen(ext);
686 n = search_getsize(dh);
687 for(i = 0; i < n; ++i)
689 s = search_getfilename(dh, i);
690 s = substring(s, 15, strlen(s) - 15 - ext_len); // strip models/sprites/ and extension
692 o = strstrofs(s, "_frame", 0);
693 sname = strcat("/spriteframes/", substring(s, 0, o));
694 sframes = substring(s, o + 6, strlen(s) - o - 6);
695 f = stof(sframes) + 1;
696 db_put(tempdb, sname, ftos(max(f, stof(db_get(tempdb, sname)))));
701 void WaypointSprite_Load()
703 waypointsprite_fadedistance = vlen(mi_scale);
704 waypointsprite_normdistance = autocvar_g_waypointsprite_normdistance;
705 waypointsprite_minscale = autocvar_g_waypointsprite_minscale;
706 waypointsprite_minalpha = autocvar_g_waypointsprite_minalpha;
707 waypointsprite_distancealphaexponent = autocvar_g_waypointsprite_distancealphaexponent;
708 waypointsprite_timealphaexponent = autocvar_g_waypointsprite_timealphaexponent;
709 waypointsprite_scale = autocvar_g_waypointsprite_scale;
710 waypointsprite_fontsize = autocvar_g_waypointsprite_fontsize;
711 waypointsprite_edgefadealpha = autocvar_g_waypointsprite_edgefadealpha;
712 waypointsprite_edgefadescale = autocvar_g_waypointsprite_edgefadescale;
713 waypointsprite_edgefadedistance = autocvar_g_waypointsprite_edgefadedistance;
714 waypointsprite_edgeoffset_bottom = autocvar_g_waypointsprite_edgeoffset_bottom;
715 waypointsprite_edgeoffset_left = autocvar_g_waypointsprite_edgeoffset_left;
716 waypointsprite_edgeoffset_right = autocvar_g_waypointsprite_edgeoffset_right;
717 waypointsprite_edgeoffset_top = autocvar_g_waypointsprite_edgeoffset_top;
718 waypointsprite_crosshairfadealpha = autocvar_g_waypointsprite_crosshairfadealpha;
719 waypointsprite_crosshairfadescale = autocvar_g_waypointsprite_crosshairfadescale;
720 waypointsprite_crosshairfadedistance = autocvar_g_waypointsprite_crosshairfadedistance;
721 waypointsprite_distancefadealpha = autocvar_g_waypointsprite_distancefadealpha;
722 waypointsprite_distancefadescale = autocvar_g_waypointsprite_distancefadescale;
723 waypointsprite_distancefadedistance = waypointsprite_fadedistance * autocvar_g_waypointsprite_distancefadedistancemultiplier;
724 waypointsprite_alpha = autocvar_g_waypointsprite_alpha * (1 - autocvar__menu_alpha);
726 if(!waypointsprite_initialized)
728 WaypointSprite_Load_Frames(".tga");
729 WaypointSprite_Load_Frames(".jpg");
730 waypointsprite_initialized = true;
733 waypointsprite_count = waypointsprite_newcount;
734 waypointsprite_newcount = 0;