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-") { return Buff_PrettyName(Buff_Type_FromSprite(s)); }
211 case "as-push": return _("Push");
212 case "as-destroy": return _("Destroy");
213 case "as-defend": return _("Defend");
214 case "bluebase": return _("Blue base");
215 case "danger": return _("DANGER");
216 case "enemyflagcarrier": return _("Enemy carrier");
217 case "flagcarrier": return _("Flag carrier");
218 case "flagdropped": return _("Dropped flag");
219 case "helpme": return _("Help me!");
220 case "here": return _("Here");
221 case "key-dropped": return _("Dropped key");
222 case "keycarrier-blue": return _("Key carrier");
223 case "keycarrier-finish": return _("Run here");
224 case "keycarrier-friend": return _("Key carrier");
225 case "keycarrier-pink": return _("Key carrier");
226 case "keycarrier-red": return _("Key carrier");
227 case "keycarrier-yellow": return _("Key carrier");
228 case "redbase": return _("Red base");
229 case "waypoint": return _("Waypoint");
230 case "ons-gen-red": return _("Generator");
231 case "ons-gen-blue": return _("Generator");
232 case "ons-gen-shielded": return _("Generator");
233 case "ons-cp-neut": return _("Control point");
234 case "ons-cp-red": return _("Control point");
235 case "ons-cp-blue": return _("Control point");
236 case "ons-cp-atck-neut": return _("Control point");
237 case "ons-cp-atck-red": return _("Control point");
238 case "ons-cp-atck-blue": return _("Control point");
239 case "ons-cp-dfnd-red": return _("Control point");
240 case "ons-cp-dfnd-blue": 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_health_mega": return _("Mega health");
255 case "item_armor_large": return _("Large armor");
256 case "item-invis": return _("Invisibility");
257 case "item-extralife": return _("Extra life");
258 case "item-speed": return _("Speed");
259 case "item-strength": return _("Strength");
260 case "item-shield": return _("Shield");
261 case "item-fuelregen": return _("Fuel regen");
262 case "item-jetpack": return _("Jet Pack");
263 case "frozen": return _("Frozen!");
264 case "tagged-target": return _("Tagged");
265 case "vehicle": return _("Vehicle");
270 vector fixrgbexcess_move(vector rgb, vector src, vector dst)
272 vector yvec = '0.299 0.587 0.114';
273 return rgb + dst * ((src * yvec) / (dst * yvec)) * ((rgb - '1 1 1') * src);
275 vector fixrgbexcess(vector rgb)
279 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 1 1');
282 rgb = fixrgbexcess_move(rgb, '0 1 0', '0 0 1');
288 rgb = fixrgbexcess_move(rgb, '0 0 1', '0 1 0');
295 rgb = fixrgbexcess_move(rgb, '0 1 0', '1 0 1');
298 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 0 1');
304 rgb = fixrgbexcess_move(rgb, '0 0 1', '1 0 0');
311 rgb = fixrgbexcess_move(rgb, '0 0 1', '1 1 0');
314 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 1 0');
320 rgb = fixrgbexcess_move(rgb, '0 1 0', '1 0 0');
328 void Draw_WaypointSprite()
334 self.alpha = pow(bound(0, (self.fadetime - time) / self.lifetime, 1), waypointsprite_timealphaexponent);
338 if(self.hideflags & 2)
339 return; // radar only
341 if(autocvar_cl_hidewaypoints >= 2)
344 if(self.hideflags & 1)
345 if(autocvar_cl_hidewaypoints)
346 return; // fixed waypoint
348 InterpolateOrigin_Do();
350 t = GetPlayerColor(player_localnum) + 1;
357 case SPRITERULE_SPECTATOR:
359 (autocvar_g_waypointsprite_itemstime == 1 && t == NUM_SPECTATOR + 1)
360 || (autocvar_g_waypointsprite_itemstime == 2 && (t == NUM_SPECTATOR + 1 || warmup_stage))
363 spriteimage = self.netname;
365 case SPRITERULE_DEFAULT:
369 spriteimage = self.netname;
374 spriteimage = self.netname;
376 case SPRITERULE_TEAMPLAY:
377 if(t == NUM_SPECTATOR + 1)
378 spriteimage = self.netname3;
379 else if(self.team == t)
380 spriteimage = self.netname2;
382 spriteimage = self.netname;
385 error("Invalid waypointsprite rule!");
389 if(spriteimage == "")
392 ++waypointsprite_newcount;
395 dist = vlen(self.origin - view_origin);
398 a = self.alpha * autocvar_hud_panel_fg_alpha;
400 if(self.maxdistance > waypointsprite_normdistance)
401 a *= pow(bound(0, (self.maxdistance - dist) / (self.maxdistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent);
402 else if(self.maxdistance > 0)
403 a *= pow(bound(0, (waypointsprite_fadedistance - dist) / (waypointsprite_fadedistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent) * (1 - waypointsprite_minalpha) + waypointsprite_minalpha;
406 rgb = self.teamradar_color;
407 rgb = spritelookupcolor(spriteimage, rgb);
410 self.teamradar_color = '1 0 1';
411 printf("WARNING: sprite of name %s has no color, using pink so you notice it\n", spriteimage);
414 if(time - floor(time) > 0.5)
416 if(self.helpme && time < self.helpme)
417 a *= SPRITE_HELPME_BLINK;
419 a *= spritelookupblinkvalue(spriteimage);
431 rgb = fixrgbexcess(rgb);
436 o = project_3d_to_2d(self.origin);
438 || o.x < (vid_conwidth * waypointsprite_edgeoffset_left)
439 || o.y < (vid_conheight * waypointsprite_edgeoffset_top)
440 || o.x > (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right))
441 || o.y > (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom)))
443 // scale it to be just in view
447 d = o - '0.5 0 0' * vid_conwidth - '0 0.5 0' * vid_conheight;
448 ang = atan2(-d.x, -d.y);
452 f1 = d.x / vid_conwidth;
453 f2 = d.y / vid_conheight;
455 if(max(f1, -f1) > max(f2, -f2))
460 d = d * ((0.5 - waypointsprite_edgeoffset_right) / f1);
465 d = d * (-(0.5 - waypointsprite_edgeoffset_left) / f1);
473 d = d * ((0.5 - waypointsprite_edgeoffset_bottom) / f2);
478 d = d * (-(0.5 - waypointsprite_edgeoffset_top) / f2);
482 o = d + '0.5 0 0' * vid_conwidth + '0 0.5 0' * vid_conheight;
490 d = o - '0.5 0 0' * vid_conwidth - '0 0.5 0' * vid_conheight;
491 ang = atan2(-d.x, -d.y);
496 float edgedistance_min, crosshairdistance;
497 edgedistance_min = min((o.y - (vid_conheight * waypointsprite_edgeoffset_top)),
498 (o.x - (vid_conwidth * waypointsprite_edgeoffset_left)),
499 (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right)) - o.x,
500 (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom)) - o.y);
503 vidscale = max(vid_conwidth / vid_width, vid_conheight / vid_height);
505 crosshairdistance = sqrt( pow(o.x - vid_conwidth/2, 2) + pow(o.y - vid_conheight/2, 2) );
507 t = waypointsprite_scale * vidscale;
508 a *= waypointsprite_alpha;
511 a = a * (1 - (1 - waypointsprite_distancefadealpha) * (bound(0, dist/waypointsprite_distancefadedistance, 1)));
512 t = t * (1 - (1 - waypointsprite_distancefadescale) * (bound(0, dist/waypointsprite_distancefadedistance, 1)));
514 if (edgedistance_min < waypointsprite_edgefadedistance) {
515 a = a * (1 - (1 - waypointsprite_edgefadealpha) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1)));
516 t = t * (1 - (1 - waypointsprite_edgefadescale) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1)));
518 if(crosshairdistance < waypointsprite_crosshairfadedistance) {
519 a = a * (1 - (1 - waypointsprite_crosshairfadealpha) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1)));
520 t = t * (1 - (1 - waypointsprite_crosshairfadescale) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1)));
523 if(self.build_finished)
525 if(time < self.build_finished + 0.25)
527 if(time < self.build_started)
528 self.health = self.build_starthealth;
529 else if(time < self.build_finished)
530 self.health = (time - self.build_started) / (self.build_finished - self.build_started) * (1 - self.build_starthealth) + self.build_starthealth;
538 o = drawspritearrow(o, ang, rgb, a, SPRITE_ARROW_SCALE * t);
541 if(autocvar_g_waypointsprite_spam && waypointsprite_count >= autocvar_g_waypointsprite_spam)
544 txt = spritelookuptext(spriteimage);
545 if(self.helpme && time < self.helpme)
546 txt = sprintf(_("%s needing help!"), txt);
547 if(autocvar_g_waypointsprite_uppercase)
548 txt = strtoupper(txt);
550 draw_beginBoldFont();
553 o = drawspritetext(o, ang, (SPRITE_HEALTHBAR_WIDTH + 2 * SPRITE_HEALTHBAR_BORDER) * t, rgb, a, waypointsprite_fontsize * '1 1 0', txt);
556 if(self.build_finished)
561 marg = -(SPRITE_HEALTHBAR_MARGIN + SPRITE_HEALTHBAR_HEIGHT + 2 * SPRITE_HEALTHBAR_BORDER) * t - 0.5 * waypointsprite_fontsize;
563 marg = SPRITE_HEALTHBAR_MARGIN * t + 0.5 * waypointsprite_fontsize;
570 SPRITE_HEALTHBAR_WIDTH * t,
571 SPRITE_HEALTHBAR_HEIGHT * t,
573 SPRITE_HEALTHBAR_BORDER * t,
576 a * SPRITE_HEALTHBAR_BORDERALPHA,
578 a * SPRITE_HEALTHBAR_HEALTHALPHA,
584 o = drawspritetext(o, ang, 0, rgb, a, waypointsprite_fontsize * '1 1 0', txt);
589 void Ent_RemoveWaypointSprite()
592 strunzone(self.netname);
594 strunzone(self.netname2);
596 strunzone(self.netname3);
599 void Ent_WaypointSprite()
602 sendflags = ReadByte();
605 self.spawntime = time;
607 self.draw2d = Draw_WaypointSprite;
609 InterpolateOrigin_Undo();
610 self.iflags |= IFLAG_ORIGIN;
617 self.health = t / 191.0;
618 self.build_finished = 0;
622 t = (t - 192) * 256 + ReadByte();
623 self.build_started = servertime;
624 if(self.build_finished)
625 self.build_starthealth = bound(0, self.health, 1);
627 self.build_starthealth = 0;
628 self.build_finished = servertime + t / 32;
634 self.build_finished = 0;
639 // unfortunately, this needs to be exact (for the 3D display)
640 self.origin_x = ReadCoord();
641 self.origin_y = ReadCoord();
642 self.origin_z = ReadCoord();
643 setorigin(self, self.origin);
648 self.team = ReadByte();
649 self.rule = ReadByte();
655 strunzone(self.netname);
656 self.netname = strzone(ReadString());
662 strunzone(self.netname2);
663 self.netname2 = strzone(ReadString());
669 strunzone(self.netname3);
670 self.netname3 = strzone(ReadString());
675 self.lifetime = ReadCoord();
676 self.fadetime = ReadCoord();
677 self.maxdistance = ReadShort();
678 self.hideflags = ReadByte();
684 self.teamradar_icon = (f & 0x7F);
687 self.(teamradar_times[self.teamradar_time_index]) = time;
688 self.teamradar_time_index = (self.teamradar_time_index + 1) % MAX_TEAMRADAR_TIMES;
690 self.teamradar_color_x = ReadByte() / 255.0;
691 self.teamradar_color_y = ReadByte() / 255.0;
692 self.teamradar_color_z = ReadByte() / 255.0;
693 self.helpme = ReadByte() * 0.1;
695 self.helpme += servertime;
698 InterpolateOrigin_Note();
700 self.entremove = Ent_RemoveWaypointSprite;
703 void WaypointSprite_Load_Frames(string ext)
705 float dh, n, i, o, f;
706 string s, sname, sframes;
707 dh = search_begin(strcat("models/sprites/*_frame*", ext), false, false);
710 float ext_len = strlen(ext);
711 n = search_getsize(dh);
712 for(i = 0; i < n; ++i)
714 s = search_getfilename(dh, i);
715 s = substring(s, 15, strlen(s) - 15 - ext_len); // strip models/sprites/ and extension
717 o = strstrofs(s, "_frame", 0);
718 sname = strcat("/spriteframes/", substring(s, 0, o));
719 sframes = substring(s, o + 6, strlen(s) - o - 6);
720 f = stof(sframes) + 1;
721 db_put(tempdb, sname, ftos(max(f, stof(db_get(tempdb, sname)))));
726 void WaypointSprite_Load()
728 waypointsprite_fadedistance = vlen(mi_scale);
729 waypointsprite_normdistance = autocvar_g_waypointsprite_normdistance;
730 waypointsprite_minscale = autocvar_g_waypointsprite_minscale;
731 waypointsprite_minalpha = autocvar_g_waypointsprite_minalpha;
732 waypointsprite_distancealphaexponent = autocvar_g_waypointsprite_distancealphaexponent;
733 waypointsprite_timealphaexponent = autocvar_g_waypointsprite_timealphaexponent;
734 waypointsprite_scale = autocvar_g_waypointsprite_scale;
735 waypointsprite_fontsize = autocvar_g_waypointsprite_fontsize;
736 waypointsprite_edgefadealpha = autocvar_g_waypointsprite_edgefadealpha;
737 waypointsprite_edgefadescale = autocvar_g_waypointsprite_edgefadescale;
738 waypointsprite_edgefadedistance = autocvar_g_waypointsprite_edgefadedistance;
739 waypointsprite_edgeoffset_bottom = autocvar_g_waypointsprite_edgeoffset_bottom;
740 waypointsprite_edgeoffset_left = autocvar_g_waypointsprite_edgeoffset_left;
741 waypointsprite_edgeoffset_right = autocvar_g_waypointsprite_edgeoffset_right;
742 waypointsprite_edgeoffset_top = autocvar_g_waypointsprite_edgeoffset_top;
743 waypointsprite_crosshairfadealpha = autocvar_g_waypointsprite_crosshairfadealpha;
744 waypointsprite_crosshairfadescale = autocvar_g_waypointsprite_crosshairfadescale;
745 waypointsprite_crosshairfadedistance = autocvar_g_waypointsprite_crosshairfadedistance;
746 waypointsprite_distancefadealpha = autocvar_g_waypointsprite_distancefadealpha;
747 waypointsprite_distancefadescale = autocvar_g_waypointsprite_distancefadescale;
748 waypointsprite_distancefadedistance = waypointsprite_fadedistance * autocvar_g_waypointsprite_distancefadedistancemultiplier;
749 waypointsprite_alpha = autocvar_g_waypointsprite_alpha * (1 - autocvar__menu_alpha);
751 if(!waypointsprite_initialized)
753 WaypointSprite_Load_Frames(".tga");
754 WaypointSprite_Load_Frames(".jpg");
755 waypointsprite_initialized = true;
758 waypointsprite_count = waypointsprite_newcount;
759 waypointsprite_newcount = 0;