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");
281 vector fixrgbexcess_move(vector rgb, vector src, vector dst)
283 vector yvec = '0.299 0.587 0.114';
284 return rgb + dst * ((src * yvec) / (dst * yvec)) * ((rgb - '1 1 1') * src);
286 vector fixrgbexcess(vector rgb)
290 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 1 1');
293 rgb = fixrgbexcess_move(rgb, '0 1 0', '0 0 1');
299 rgb = fixrgbexcess_move(rgb, '0 0 1', '0 1 0');
306 rgb = fixrgbexcess_move(rgb, '0 1 0', '1 0 1');
309 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 0 1');
315 rgb = fixrgbexcess_move(rgb, '0 0 1', '1 0 0');
322 rgb = fixrgbexcess_move(rgb, '0 0 1', '1 1 0');
325 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 1 0');
331 rgb = fixrgbexcess_move(rgb, '0 1 0', '1 0 0');
339 void Draw_WaypointSprite()
345 self.alpha = pow(bound(0, (self.fadetime - time) / self.lifetime, 1), waypointsprite_timealphaexponent);
349 if(self.hideflags & 2)
350 return; // radar only
352 if(autocvar_cl_hidewaypoints >= 2)
355 if(self.hideflags & 1)
356 if(autocvar_cl_hidewaypoints)
357 return; // fixed waypoint
359 InterpolateOrigin_Do();
361 t = GetPlayerColor(player_localnum) + 1;
368 case SPRITERULE_SPECTATOR:
370 (autocvar_g_waypointsprite_itemstime == 1 && t == NUM_SPECTATOR + 1)
371 || (autocvar_g_waypointsprite_itemstime == 2 && (t == NUM_SPECTATOR + 1 || warmup_stage))
374 spriteimage = self.netname;
376 case SPRITERULE_DEFAULT:
380 spriteimage = self.netname;
385 spriteimage = self.netname;
387 case SPRITERULE_TEAMPLAY:
388 if(t == NUM_SPECTATOR + 1)
389 spriteimage = self.netname3;
390 else if(self.team == t)
391 spriteimage = self.netname2;
393 spriteimage = self.netname;
396 error("Invalid waypointsprite rule!");
400 if(spriteimage == "")
403 ++waypointsprite_newcount;
406 dist = vlen(self.origin - view_origin);
409 a = self.alpha * autocvar_hud_panel_fg_alpha;
411 if(self.maxdistance > waypointsprite_normdistance)
412 a *= pow(bound(0, (self.maxdistance - dist) / (self.maxdistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent);
413 else if(self.maxdistance > 0)
414 a *= pow(bound(0, (waypointsprite_fadedistance - dist) / (waypointsprite_fadedistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent) * (1 - waypointsprite_minalpha) + waypointsprite_minalpha;
417 rgb = self.teamradar_color;
418 rgb = spritelookupcolor(spriteimage, rgb);
421 self.teamradar_color = '1 0 1';
422 printf("WARNING: sprite of name %s has no color, using pink so you notice it\n", spriteimage);
425 if(time - floor(time) > 0.5)
427 if(self.helpme && time < self.helpme)
428 a *= SPRITE_HELPME_BLINK;
429 else if(!self.lifetime) // fading out waypoints don't blink
430 a *= spritelookupblinkvalue(spriteimage);
442 rgb = fixrgbexcess(rgb);
447 o = project_3d_to_2d(self.origin);
449 || o.x < (vid_conwidth * waypointsprite_edgeoffset_left)
450 || o.y < (vid_conheight * waypointsprite_edgeoffset_top)
451 || o.x > (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right))
452 || o.y > (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom)))
454 // scale it to be just in view
458 d = o - '0.5 0 0' * vid_conwidth - '0 0.5 0' * vid_conheight;
459 ang = atan2(-d.x, -d.y);
463 f1 = d.x / vid_conwidth;
464 f2 = d.y / vid_conheight;
466 if(max(f1, -f1) > max(f2, -f2))
471 d = d * ((0.5 - waypointsprite_edgeoffset_right) / f1);
476 d = d * (-(0.5 - waypointsprite_edgeoffset_left) / f1);
484 d = d * ((0.5 - waypointsprite_edgeoffset_bottom) / f2);
489 d = d * (-(0.5 - waypointsprite_edgeoffset_top) / f2);
493 o = d + '0.5 0 0' * vid_conwidth + '0 0.5 0' * vid_conheight;
501 d = o - '0.5 0 0' * vid_conwidth - '0 0.5 0' * vid_conheight;
502 ang = atan2(-d.x, -d.y);
507 float edgedistance_min, crosshairdistance;
508 edgedistance_min = min((o.y - (vid_conheight * waypointsprite_edgeoffset_top)),
509 (o.x - (vid_conwidth * waypointsprite_edgeoffset_left)),
510 (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right)) - o.x,
511 (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom)) - o.y);
514 vidscale = max(vid_conwidth / vid_width, vid_conheight / vid_height);
516 crosshairdistance = sqrt( pow(o.x - vid_conwidth/2, 2) + pow(o.y - vid_conheight/2, 2) );
518 t = waypointsprite_scale * vidscale;
519 a *= waypointsprite_alpha;
522 a = a * (1 - (1 - waypointsprite_distancefadealpha) * (bound(0, dist/waypointsprite_distancefadedistance, 1)));
523 t = t * (1 - (1 - waypointsprite_distancefadescale) * (bound(0, dist/waypointsprite_distancefadedistance, 1)));
525 if (edgedistance_min < waypointsprite_edgefadedistance) {
526 a = a * (1 - (1 - waypointsprite_edgefadealpha) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1)));
527 t = t * (1 - (1 - waypointsprite_edgefadescale) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1)));
529 if(crosshairdistance < waypointsprite_crosshairfadedistance) {
530 a = a * (1 - (1 - waypointsprite_crosshairfadealpha) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1)));
531 t = t * (1 - (1 - waypointsprite_crosshairfadescale) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1)));
534 if(self.build_finished)
536 if(time < self.build_finished + 0.25)
538 if(time < self.build_started)
539 self.health = self.build_starthealth;
540 else if(time < self.build_finished)
541 self.health = (time - self.build_started) / (self.build_finished - self.build_started) * (1 - self.build_starthealth) + self.build_starthealth;
549 o = drawspritearrow(o, ang, rgb, a, SPRITE_ARROW_SCALE * t);
552 if(autocvar_g_waypointsprite_spam && waypointsprite_count >= autocvar_g_waypointsprite_spam)
555 txt = spritelookuptext(spriteimage);
556 if(self.helpme && time < self.helpme)
557 txt = sprintf(_("%s needing help!"), txt);
558 if(autocvar_g_waypointsprite_uppercase)
559 txt = strtoupper(txt);
561 draw_beginBoldFont();
564 o = drawspritetext(o, ang, (SPRITE_HEALTHBAR_WIDTH + 2 * SPRITE_HEALTHBAR_BORDER) * t, rgb, a, waypointsprite_fontsize * '1 1 0', txt);
567 if(self.build_finished)
572 marg = -(SPRITE_HEALTHBAR_MARGIN + SPRITE_HEALTHBAR_HEIGHT + 2 * SPRITE_HEALTHBAR_BORDER) * t - 0.5 * waypointsprite_fontsize;
574 marg = SPRITE_HEALTHBAR_MARGIN * t + 0.5 * waypointsprite_fontsize;
581 SPRITE_HEALTHBAR_WIDTH * t,
582 SPRITE_HEALTHBAR_HEIGHT * t,
584 SPRITE_HEALTHBAR_BORDER * t,
587 a * SPRITE_HEALTHBAR_BORDERALPHA,
589 a * SPRITE_HEALTHBAR_HEALTHALPHA,
595 o = drawspritetext(o, ang, 0, rgb, a, waypointsprite_fontsize * '1 1 0', txt);
600 void Ent_RemoveWaypointSprite()
603 strunzone(self.netname);
605 strunzone(self.netname2);
607 strunzone(self.netname3);
610 void Ent_WaypointSprite()
613 sendflags = ReadByte();
616 self.spawntime = time;
618 self.draw2d = Draw_WaypointSprite;
620 InterpolateOrigin_Undo();
621 self.iflags |= IFLAG_ORIGIN;
628 self.health = t / 191.0;
629 self.build_finished = 0;
633 t = (t - 192) * 256 + ReadByte();
634 self.build_started = servertime;
635 if(self.build_finished)
636 self.build_starthealth = bound(0, self.health, 1);
638 self.build_starthealth = 0;
639 self.build_finished = servertime + t / 32;
645 self.build_finished = 0;
650 // unfortunately, this needs to be exact (for the 3D display)
651 self.origin_x = ReadCoord();
652 self.origin_y = ReadCoord();
653 self.origin_z = ReadCoord();
654 setorigin(self, self.origin);
659 self.team = ReadByte();
660 self.rule = ReadByte();
666 strunzone(self.netname);
667 self.netname = strzone(ReadString());
673 strunzone(self.netname2);
674 self.netname2 = strzone(ReadString());
680 strunzone(self.netname3);
681 self.netname3 = strzone(ReadString());
686 self.lifetime = ReadCoord();
687 self.fadetime = ReadCoord();
688 self.maxdistance = ReadShort();
689 self.hideflags = ReadByte();
695 self.teamradar_icon = (f & 0x7F);
698 self.(teamradar_times[self.teamradar_time_index]) = time;
699 self.teamradar_time_index = (self.teamradar_time_index + 1) % MAX_TEAMRADAR_TIMES;
701 self.teamradar_color_x = ReadByte() / 255.0;
702 self.teamradar_color_y = ReadByte() / 255.0;
703 self.teamradar_color_z = ReadByte() / 255.0;
704 self.helpme = ReadByte() * 0.1;
706 self.helpme += servertime;
709 InterpolateOrigin_Note();
711 self.entremove = Ent_RemoveWaypointSprite;
714 void WaypointSprite_Load_Frames(string ext)
716 float dh, n, i, o, f;
717 string s, sname, sframes;
718 dh = search_begin(strcat("models/sprites/*_frame*", ext), false, false);
721 float ext_len = strlen(ext);
722 n = search_getsize(dh);
723 for(i = 0; i < n; ++i)
725 s = search_getfilename(dh, i);
726 s = substring(s, 15, strlen(s) - 15 - ext_len); // strip models/sprites/ and extension
728 o = strstrofs(s, "_frame", 0);
729 sname = strcat("/spriteframes/", substring(s, 0, o));
730 sframes = substring(s, o + 6, strlen(s) - o - 6);
731 f = stof(sframes) + 1;
732 db_put(tempdb, sname, ftos(max(f, stof(db_get(tempdb, sname)))));
737 void WaypointSprite_Load()
739 waypointsprite_fadedistance = vlen(mi_scale);
740 waypointsprite_normdistance = autocvar_g_waypointsprite_normdistance;
741 waypointsprite_minscale = autocvar_g_waypointsprite_minscale;
742 waypointsprite_minalpha = autocvar_g_waypointsprite_minalpha;
743 waypointsprite_distancealphaexponent = autocvar_g_waypointsprite_distancealphaexponent;
744 waypointsprite_timealphaexponent = autocvar_g_waypointsprite_timealphaexponent;
745 waypointsprite_scale = autocvar_g_waypointsprite_scale;
746 waypointsprite_fontsize = autocvar_g_waypointsprite_fontsize;
747 waypointsprite_edgefadealpha = autocvar_g_waypointsprite_edgefadealpha;
748 waypointsprite_edgefadescale = autocvar_g_waypointsprite_edgefadescale;
749 waypointsprite_edgefadedistance = autocvar_g_waypointsprite_edgefadedistance;
750 waypointsprite_edgeoffset_bottom = autocvar_g_waypointsprite_edgeoffset_bottom;
751 waypointsprite_edgeoffset_left = autocvar_g_waypointsprite_edgeoffset_left;
752 waypointsprite_edgeoffset_right = autocvar_g_waypointsprite_edgeoffset_right;
753 waypointsprite_edgeoffset_top = autocvar_g_waypointsprite_edgeoffset_top;
754 waypointsprite_crosshairfadealpha = autocvar_g_waypointsprite_crosshairfadealpha;
755 waypointsprite_crosshairfadescale = autocvar_g_waypointsprite_crosshairfadescale;
756 waypointsprite_crosshairfadedistance = autocvar_g_waypointsprite_crosshairfadedistance;
757 waypointsprite_distancefadealpha = autocvar_g_waypointsprite_distancefadealpha;
758 waypointsprite_distancefadescale = autocvar_g_waypointsprite_distancefadescale;
759 waypointsprite_distancefadedistance = waypointsprite_fadedistance * autocvar_g_waypointsprite_distancefadedistancemultiplier;
760 waypointsprite_alpha = autocvar_g_waypointsprite_alpha * (1 - autocvar__menu_alpha);
762 if(!waypointsprite_initialized)
764 WaypointSprite_Load_Frames(".tga");
765 WaypointSprite_Load_Frames(".jpg");
766 waypointsprite_initialized = true;
769 waypointsprite_count = waypointsprite_newcount;
770 waypointsprite_newcount = 0;