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 "yellowbase": return _("Yellow base");
230 case "neutralbase": return _("White base");
231 case "pinkbase": return _("Pink base");
232 case "waypoint": return _("Waypoint");
233 case "ons-gen-red": return _("Generator");
234 case "ons-gen-blue": return _("Generator");
235 case "ons-gen-shielded": return _("Generator");
236 case "ons-cp-neut": return _("Control point");
237 case "ons-cp-red": return _("Control point");
238 case "ons-cp-blue": return _("Control point");
239 case "ons-cp-atck-neut": return _("Control point");
240 case "ons-cp-atck-red": return _("Control point");
241 case "ons-cp-atck-blue": return _("Control point");
242 case "ons-cp-dfnd-red": return _("Control point");
243 case "ons-cp-dfnd-blue": return _("Control point");
244 case "race-checkpoint": return _("Checkpoint");
245 case "race-finish": return _("Finish");
246 case "race-start": return _("Start");
247 case "race-start-finish": return (race_checkpointtime || race_mycheckpointtime) ? _("Finish") : _("Start");
248 case "goal": return _("Goal");
249 case "nb-ball": return _("Ball");
250 case "ka-ball": return _("Ball");
251 case "ka-ballcarrier": return _("Ball carrier");
252 case "dom-neut": return _("Control point");
253 case "dom-red": return _("Control point");
254 case "dom-blue": return _("Control point");
255 case "dom-yellow": return _("Control point");
256 case "dom-pink": return _("Control point");
257 case "item_health_mega": return _("Mega health");
258 case "item_armor_large": return _("Large armor");
259 case "item-invis": return _("Invisibility");
260 case "item-extralife": return _("Extra life");
261 case "item-speed": return _("Speed");
262 case "item-strength": return _("Strength");
263 case "item-shield": return _("Shield");
264 case "item-fuelregen": return _("Fuel regen");
265 case "item-jetpack": return _("Jet Pack");
266 case "frozen": return _("Frozen!");
267 case "tagged-target": return _("Tagged");
268 case "vehicle": return _("Vehicle");
273 vector fixrgbexcess_move(vector rgb, vector src, vector dst)
275 vector yvec = '0.299 0.587 0.114';
276 return rgb + dst * ((src * yvec) / (dst * yvec)) * ((rgb - '1 1 1') * src);
278 vector fixrgbexcess(vector rgb)
282 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 1 1');
285 rgb = fixrgbexcess_move(rgb, '0 1 0', '0 0 1');
291 rgb = fixrgbexcess_move(rgb, '0 0 1', '0 1 0');
298 rgb = fixrgbexcess_move(rgb, '0 1 0', '1 0 1');
301 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 0 1');
307 rgb = fixrgbexcess_move(rgb, '0 0 1', '1 0 0');
314 rgb = fixrgbexcess_move(rgb, '0 0 1', '1 1 0');
317 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 1 0');
323 rgb = fixrgbexcess_move(rgb, '0 1 0', '1 0 0');
331 void Draw_WaypointSprite()
337 self.alpha = pow(bound(0, (self.fadetime - time) / self.lifetime, 1), waypointsprite_timealphaexponent);
341 if(self.hideflags & 2)
342 return; // radar only
344 if(autocvar_cl_hidewaypoints >= 2)
347 if(self.hideflags & 1)
348 if(autocvar_cl_hidewaypoints)
349 return; // fixed waypoint
351 InterpolateOrigin_Do();
353 t = GetPlayerColor(player_localnum) + 1;
360 case SPRITERULE_SPECTATOR:
362 (autocvar_g_waypointsprite_itemstime == 1 && t == NUM_SPECTATOR + 1)
363 || (autocvar_g_waypointsprite_itemstime == 2 && (t == NUM_SPECTATOR + 1 || warmup_stage))
366 spriteimage = self.netname;
368 case SPRITERULE_DEFAULT:
372 spriteimage = self.netname;
377 spriteimage = self.netname;
379 case SPRITERULE_TEAMPLAY:
380 if(t == NUM_SPECTATOR + 1)
381 spriteimage = self.netname3;
382 else if(self.team == t)
383 spriteimage = self.netname2;
385 spriteimage = self.netname;
388 error("Invalid waypointsprite rule!");
392 if(spriteimage == "")
395 ++waypointsprite_newcount;
398 dist = vlen(self.origin - view_origin);
401 a = self.alpha * autocvar_hud_panel_fg_alpha;
403 if(self.maxdistance > waypointsprite_normdistance)
404 a *= pow(bound(0, (self.maxdistance - dist) / (self.maxdistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent);
405 else if(self.maxdistance > 0)
406 a *= pow(bound(0, (waypointsprite_fadedistance - dist) / (waypointsprite_fadedistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent) * (1 - waypointsprite_minalpha) + waypointsprite_minalpha;
409 rgb = self.teamradar_color;
410 rgb = spritelookupcolor(spriteimage, rgb);
413 self.teamradar_color = '1 0 1';
414 printf("WARNING: sprite of name %s has no color, using pink so you notice it\n", spriteimage);
417 if(time - floor(time) > 0.5)
419 if(self.helpme && time < self.helpme)
420 a *= SPRITE_HELPME_BLINK;
421 else if(!self.lifetime) // fading out waypoints don't blink
422 a *= spritelookupblinkvalue(spriteimage);
434 rgb = fixrgbexcess(rgb);
439 o = project_3d_to_2d(self.origin);
441 || o.x < (vid_conwidth * waypointsprite_edgeoffset_left)
442 || o.y < (vid_conheight * waypointsprite_edgeoffset_top)
443 || o.x > (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right))
444 || o.y > (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom)))
446 // scale it to be just in view
450 d = o - '0.5 0 0' * vid_conwidth - '0 0.5 0' * vid_conheight;
451 ang = atan2(-d.x, -d.y);
455 f1 = d.x / vid_conwidth;
456 f2 = d.y / vid_conheight;
458 if(max(f1, -f1) > max(f2, -f2))
463 d = d * ((0.5 - waypointsprite_edgeoffset_right) / f1);
468 d = d * (-(0.5 - waypointsprite_edgeoffset_left) / f1);
476 d = d * ((0.5 - waypointsprite_edgeoffset_bottom) / f2);
481 d = d * (-(0.5 - waypointsprite_edgeoffset_top) / f2);
485 o = d + '0.5 0 0' * vid_conwidth + '0 0.5 0' * vid_conheight;
493 d = o - '0.5 0 0' * vid_conwidth - '0 0.5 0' * vid_conheight;
494 ang = atan2(-d.x, -d.y);
499 float edgedistance_min, crosshairdistance;
500 edgedistance_min = min((o.y - (vid_conheight * waypointsprite_edgeoffset_top)),
501 (o.x - (vid_conwidth * waypointsprite_edgeoffset_left)),
502 (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right)) - o.x,
503 (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom)) - o.y);
506 vidscale = max(vid_conwidth / vid_width, vid_conheight / vid_height);
508 crosshairdistance = sqrt( pow(o.x - vid_conwidth/2, 2) + pow(o.y - vid_conheight/2, 2) );
510 t = waypointsprite_scale * vidscale;
511 a *= waypointsprite_alpha;
514 a = a * (1 - (1 - waypointsprite_distancefadealpha) * (bound(0, dist/waypointsprite_distancefadedistance, 1)));
515 t = t * (1 - (1 - waypointsprite_distancefadescale) * (bound(0, dist/waypointsprite_distancefadedistance, 1)));
517 if (edgedistance_min < waypointsprite_edgefadedistance) {
518 a = a * (1 - (1 - waypointsprite_edgefadealpha) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1)));
519 t = t * (1 - (1 - waypointsprite_edgefadescale) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1)));
521 if(crosshairdistance < waypointsprite_crosshairfadedistance) {
522 a = a * (1 - (1 - waypointsprite_crosshairfadealpha) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1)));
523 t = t * (1 - (1 - waypointsprite_crosshairfadescale) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1)));
526 if(self.build_finished)
528 if(time < self.build_finished + 0.25)
530 if(time < self.build_started)
531 self.health = self.build_starthealth;
532 else if(time < self.build_finished)
533 self.health = (time - self.build_started) / (self.build_finished - self.build_started) * (1 - self.build_starthealth) + self.build_starthealth;
541 o = drawspritearrow(o, ang, rgb, a, SPRITE_ARROW_SCALE * t);
544 if(autocvar_g_waypointsprite_spam && waypointsprite_count >= autocvar_g_waypointsprite_spam)
547 txt = spritelookuptext(spriteimage);
548 if(self.helpme && time < self.helpme)
549 txt = sprintf(_("%s needing help!"), txt);
550 if(autocvar_g_waypointsprite_uppercase)
551 txt = strtoupper(txt);
553 draw_beginBoldFont();
556 o = drawspritetext(o, ang, (SPRITE_HEALTHBAR_WIDTH + 2 * SPRITE_HEALTHBAR_BORDER) * t, rgb, a, waypointsprite_fontsize * '1 1 0', txt);
559 if(self.build_finished)
564 marg = -(SPRITE_HEALTHBAR_MARGIN + SPRITE_HEALTHBAR_HEIGHT + 2 * SPRITE_HEALTHBAR_BORDER) * t - 0.5 * waypointsprite_fontsize;
566 marg = SPRITE_HEALTHBAR_MARGIN * t + 0.5 * waypointsprite_fontsize;
573 SPRITE_HEALTHBAR_WIDTH * t,
574 SPRITE_HEALTHBAR_HEIGHT * t,
576 SPRITE_HEALTHBAR_BORDER * t,
579 a * SPRITE_HEALTHBAR_BORDERALPHA,
581 a * SPRITE_HEALTHBAR_HEALTHALPHA,
587 o = drawspritetext(o, ang, 0, rgb, a, waypointsprite_fontsize * '1 1 0', txt);
592 void Ent_RemoveWaypointSprite()
595 strunzone(self.netname);
597 strunzone(self.netname2);
599 strunzone(self.netname3);
602 void Ent_WaypointSprite()
605 sendflags = ReadByte();
608 self.spawntime = time;
610 self.draw2d = Draw_WaypointSprite;
612 InterpolateOrigin_Undo();
613 self.iflags |= IFLAG_ORIGIN;
620 self.health = t / 191.0;
621 self.build_finished = 0;
625 t = (t - 192) * 256 + ReadByte();
626 self.build_started = servertime;
627 if(self.build_finished)
628 self.build_starthealth = bound(0, self.health, 1);
630 self.build_starthealth = 0;
631 self.build_finished = servertime + t / 32;
637 self.build_finished = 0;
642 // unfortunately, this needs to be exact (for the 3D display)
643 self.origin_x = ReadCoord();
644 self.origin_y = ReadCoord();
645 self.origin_z = ReadCoord();
646 setorigin(self, self.origin);
651 self.team = ReadByte();
652 self.rule = ReadByte();
658 strunzone(self.netname);
659 self.netname = strzone(ReadString());
665 strunzone(self.netname2);
666 self.netname2 = strzone(ReadString());
672 strunzone(self.netname3);
673 self.netname3 = strzone(ReadString());
678 self.lifetime = ReadCoord();
679 self.fadetime = ReadCoord();
680 self.maxdistance = ReadShort();
681 self.hideflags = ReadByte();
687 self.teamradar_icon = (f & 0x7F);
690 self.(teamradar_times[self.teamradar_time_index]) = time;
691 self.teamradar_time_index = (self.teamradar_time_index + 1) % MAX_TEAMRADAR_TIMES;
693 self.teamradar_color_x = ReadByte() / 255.0;
694 self.teamradar_color_y = ReadByte() / 255.0;
695 self.teamradar_color_z = ReadByte() / 255.0;
696 self.helpme = ReadByte() * 0.1;
698 self.helpme += servertime;
701 InterpolateOrigin_Note();
703 self.entremove = Ent_RemoveWaypointSprite;
706 void WaypointSprite_Load_Frames(string ext)
708 float dh, n, i, o, f;
709 string s, sname, sframes;
710 dh = search_begin(strcat("models/sprites/*_frame*", ext), false, false);
713 float ext_len = strlen(ext);
714 n = search_getsize(dh);
715 for(i = 0; i < n; ++i)
717 s = search_getfilename(dh, i);
718 s = substring(s, 15, strlen(s) - 15 - ext_len); // strip models/sprites/ and extension
720 o = strstrofs(s, "_frame", 0);
721 sname = strcat("/spriteframes/", substring(s, 0, o));
722 sframes = substring(s, o + 6, strlen(s) - o - 6);
723 f = stof(sframes) + 1;
724 db_put(tempdb, sname, ftos(max(f, stof(db_get(tempdb, sname)))));
729 void WaypointSprite_Load()
731 waypointsprite_fadedistance = vlen(mi_scale);
732 waypointsprite_normdistance = autocvar_g_waypointsprite_normdistance;
733 waypointsprite_minscale = autocvar_g_waypointsprite_minscale;
734 waypointsprite_minalpha = autocvar_g_waypointsprite_minalpha;
735 waypointsprite_distancealphaexponent = autocvar_g_waypointsprite_distancealphaexponent;
736 waypointsprite_timealphaexponent = autocvar_g_waypointsprite_timealphaexponent;
737 waypointsprite_scale = autocvar_g_waypointsprite_scale;
738 waypointsprite_fontsize = autocvar_g_waypointsprite_fontsize;
739 waypointsprite_edgefadealpha = autocvar_g_waypointsprite_edgefadealpha;
740 waypointsprite_edgefadescale = autocvar_g_waypointsprite_edgefadescale;
741 waypointsprite_edgefadedistance = autocvar_g_waypointsprite_edgefadedistance;
742 waypointsprite_edgeoffset_bottom = autocvar_g_waypointsprite_edgeoffset_bottom;
743 waypointsprite_edgeoffset_left = autocvar_g_waypointsprite_edgeoffset_left;
744 waypointsprite_edgeoffset_right = autocvar_g_waypointsprite_edgeoffset_right;
745 waypointsprite_edgeoffset_top = autocvar_g_waypointsprite_edgeoffset_top;
746 waypointsprite_crosshairfadealpha = autocvar_g_waypointsprite_crosshairfadealpha;
747 waypointsprite_crosshairfadescale = autocvar_g_waypointsprite_crosshairfadescale;
748 waypointsprite_crosshairfadedistance = autocvar_g_waypointsprite_crosshairfadedistance;
749 waypointsprite_distancefadealpha = autocvar_g_waypointsprite_distancefadealpha;
750 waypointsprite_distancefadescale = autocvar_g_waypointsprite_distancefadescale;
751 waypointsprite_distancefadedistance = waypointsprite_fadedistance * autocvar_g_waypointsprite_distancefadedistancemultiplier;
752 waypointsprite_alpha = autocvar_g_waypointsprite_alpha * (1 - autocvar__menu_alpha);
754 if(!waypointsprite_initialized)
756 WaypointSprite_Load_Frames(".tga");
757 WaypointSprite_Load_Frames(".jpg");
758 waypointsprite_initialized = true;
761 waypointsprite_count = waypointsprite_newcount;
762 waypointsprite_newcount = 0;