1 #include "waypointsprites.qh"
3 void drawrotpic(vector org, float rot, string pic, vector sz, vector hotspot, vector rgb, float a, float f)
7 hotspot = -1 * hotspot;
9 // hotspot-relative coordinates of the corners
11 v2 = hotspot + '1 0 0' * sz.x;
12 v3 = hotspot + '1 0 0' * sz.x + '0 1 0' * sz.y;
13 v4 = hotspot + '0 1 0' * sz.y;
15 // rotate them, and make them absolute
16 rot = -rot; // rotate by the opposite angle, as our coordinate system is reversed
17 v1 = rotate(v1, rot) + org;
18 v2 = rotate(v2, rot) + org;
19 v3 = rotate(v3, rot) + org;
20 v4 = rotate(v4, rot) + org;
23 R_BeginPolygon(pic, f);
24 R_PolygonVertex(v1, '0 0 0', rgb, a);
25 R_PolygonVertex(v2, '1 0 0', rgb, a);
26 R_PolygonVertex(v3, '1 1 0', rgb, a);
27 R_PolygonVertex(v4, '0 1 0', rgb, a);
31 void drawquad(vector o, vector ri, vector up, string pic, vector rgb, float a, float f)
33 R_BeginPolygon(pic, f);
34 R_PolygonVertex(o, '0 0 0', rgb, a);
35 R_PolygonVertex(o + ri, '1 0 0', rgb, a);
36 R_PolygonVertex(o + up + ri, '1 1 0', rgb, a);
37 R_PolygonVertex(o + up, '0 1 0', rgb, a);
41 void drawhealthbar(vector org, float rot, float h, vector sz, vector hotspot, float width, float height, float margin, float border, float align, vector rgb, float a, vector hrgb, float ha, float f)
44 float owidth; // outer width
46 hotspot = -1 * hotspot;
48 // hotspot-relative coordinates of the healthbar corners
53 rot = -rot; // rotate by the opposite angle, as our coordinate system is reversed
54 o = rotate(o, rot) + org;
58 owidth = width + 2 * border;
59 o = o - up * (margin + border + height) + ri * (sz.x - owidth) * 0.5;
61 drawquad(o - up * border, ri * owidth, up * border, "", rgb, a, f);
62 drawquad(o + up * height, ri * owidth, up * border, "", rgb, a, f);
63 drawquad(o, ri * border, up * height, "", rgb, a, f);
64 drawquad(o + ri * (owidth - border), ri * border, up * height, "", rgb, a, f);
65 drawquad(o + ri * (border + align * ((1 - h) * width)), ri * width * h, up * height, "", hrgb, ha, f);
68 // returns location of sprite text
69 vector drawspritearrow(vector o, float ang, vector rgb, float a, float t)
72 float border = 1.5 * t;
73 float margin = 4.0 * t;
75 float borderDiag = border * 1.414;
76 vector arrowX = eX * size;
77 vector arrowY = eY * (size+borderDiag);
78 vector borderX = eX * (size+borderDiag);
79 vector borderY = eY * (size+borderDiag+border);
81 R_BeginPolygon("", DRAWFLAG_NORMAL);
82 R_PolygonVertex(o, '0 0 0', '0 0 0', a);
83 R_PolygonVertex(o + rotate(arrowY - borderX, ang), '0 0 0', '0 0 0', a);
84 R_PolygonVertex(o + rotate(borderY - borderX, ang), '0 0 0', '0 0 0', a);
85 R_PolygonVertex(o + rotate(borderY + borderX, ang), '0 0 0', '0 0 0', a);
86 R_PolygonVertex(o + rotate(arrowY + borderX, ang), '0 0 0', '0 0 0', a);
89 R_BeginPolygon("", DRAWFLAG_ADDITIVE);
90 R_PolygonVertex(o + rotate(eY * borderDiag, ang), '0 0 0', rgb, a);
91 R_PolygonVertex(o + rotate(arrowY - arrowX, ang), '0 0 0', rgb, a);
92 R_PolygonVertex(o + rotate(arrowY + arrowX, ang), '0 0 0', rgb, a);
95 return o + rotate(eY * (borderDiag+size+margin), ang);
98 // returns location of sprite healthbar
99 vector drawspritetext(vector o, float ang, float minwidth, vector rgb, float a, vector fontsize, string s)
103 float aspect, sa, ca;
105 sw = stringwidth(s, false, fontsize);
112 // how do corners work?
113 aspect = vid_conwidth / vid_conheight;
115 ca = cos(ang) * aspect;
116 if(fabs(sa) > fabs(ca))
119 algny = 0.5 - 0.5 * ca / fabs(sa);
123 algnx = 0.5 - 0.5 * sa / fabs(ca);
131 // we want to be onscreen
136 if(o.x > vid_conwidth - w)
137 o.x = vid_conwidth - w;
138 if(o.y > vid_conheight - h)
139 o.x = vid_conheight - h;
141 o.x += 0.5 * (w - sw);
143 drawstring(o, s, fontsize, rgb, a, DRAWFLAG_NORMAL);
151 float spritelookupblinkvalue(string s)
155 case "ons-cp-atck-neut": return 2;
156 case "ons-cp-atck-red": return 2;
157 case "ons-cp-atck-blue": return 2;
158 case "ons-cp-dfnd-red": return 0.5;
159 case "ons-cp-dfnd-blue": return 0.5;
160 case "item-invis": return 2;
161 case "item-extralife": return 2;
162 case "item-speed": return 2;
163 case "item-strength": return 2;
164 case "item-shield": return 2;
165 case "item-fuelregen": return 2;
166 case "item-jetpack": return 2;
167 case "tagged-target": return 2;
171 vector spritelookupcolor(string s, vector def)
173 if(substring(s, 0, 4) == "wpn-")
174 return (get_weaponinfo(stof(substring(s, 4, strlen(s)))).wpcolor);
178 case "keycarrier-friend": return '0 1 0';
182 string spritelookuptext(string s)
184 if(substring(s, 0, 4) == "wpn-") { return (get_weaponinfo(stof(substring(s, 4, strlen(s)))).message); }
185 if(substring(s, 0, 5) == "buff-") { return Buff_PrettyName(Buff_Type_FromSprite(s)); }
189 case "as-push": return _("Push");
190 case "as-destroy": return _("Destroy");
191 case "as-defend": return _("Defend");
192 case "bluebase": return _("Blue base");
193 case "danger": return _("DANGER");
194 case "enemyflagcarrier": return _("Enemy carrier");
195 case "flagcarrier": return _("Flag carrier");
196 case "flagdropped": return _("Dropped flag");
197 case "helpme": return _("Help me!");
198 case "here": return _("Here");
199 case "key-dropped": return _("Dropped key");
200 case "keycarrier-blue": return _("Key carrier");
201 case "keycarrier-finish": return _("Run here");
202 case "keycarrier-friend": return _("Key carrier");
203 case "keycarrier-pink": return _("Key carrier");
204 case "keycarrier-red": return _("Key carrier");
205 case "keycarrier-yellow": return _("Key carrier");
206 case "redbase": return _("Red base");
207 case "waypoint": return _("Waypoint");
208 case "ons-gen-red": return _("Generator");
209 case "ons-gen-blue": return _("Generator");
210 case "ons-gen-shielded": return _("Generator");
211 case "ons-cp-neut": return _("Control point");
212 case "ons-cp-red": return _("Control point");
213 case "ons-cp-blue": return _("Control point");
214 case "ons-cp-atck-neut": return _("Control point");
215 case "ons-cp-atck-red": return _("Control point");
216 case "ons-cp-atck-blue": return _("Control point");
217 case "ons-cp-dfnd-red": return _("Control point");
218 case "ons-cp-dfnd-blue": return _("Control point");
219 case "race-checkpoint": return _("Checkpoint");
220 case "race-finish": return _("Finish");
221 case "race-start": return _("Start");
222 case "race-start-finish": return (race_checkpointtime || race_mycheckpointtime) ? _("Finish") : _("Start");
223 case "goal": return _("Goal");
224 case "nb-ball": return _("Ball");
225 case "ka-ball": return _("Ball");
226 case "ka-ballcarrier": return _("Ball carrier");
227 case "dom-neut": return _("Control point");
228 case "dom-red": return _("Control point");
229 case "dom-blue": return _("Control point");
230 case "dom-yellow": return _("Control point");
231 case "dom-pink": return _("Control point");
232 case "item-invis": return _("Invisibility");
233 case "item-extralife": return _("Extra life");
234 case "item-speed": return _("Speed");
235 case "item-strength": return _("Strength");
236 case "item-shield": return _("Shield");
237 case "item-fuelregen": return _("Fuel regen");
238 case "item-jetpack": return _("Jet Pack");
239 case "frozen": return _("Frozen!");
240 case "tagged-target": return _("Tagged");
241 case "vehicle": return _("Vehicle");
246 vector fixrgbexcess_move(vector rgb, vector src, vector dst)
248 vector yvec = '0.299 0.587 0.114';
249 return rgb + dst * ((src * yvec) / (dst * yvec)) * ((rgb - '1 1 1') * src);
251 vector fixrgbexcess(vector rgb)
255 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 1 1');
258 rgb = fixrgbexcess_move(rgb, '0 1 0', '0 0 1');
264 rgb = fixrgbexcess_move(rgb, '0 0 1', '0 1 0');
271 rgb = fixrgbexcess_move(rgb, '0 1 0', '1 0 1');
274 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 0 1');
280 rgb = fixrgbexcess_move(rgb, '0 0 1', '1 0 0');
287 rgb = fixrgbexcess_move(rgb, '0 0 1', '1 1 0');
290 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 1 0');
296 rgb = fixrgbexcess_move(rgb, '0 1 0', '1 0 0');
304 void Draw_WaypointSprite()
310 self.alpha = pow(bound(0, (self.fadetime - time) / self.lifetime, 1), waypointsprite_timealphaexponent);
314 if(self.hideflags & 2)
315 return; // radar only
317 if(autocvar_cl_hidewaypoints >= 2)
320 if(self.hideflags & 1)
321 if(autocvar_cl_hidewaypoints)
322 return; // fixed waypoint
324 InterpolateOrigin_Do();
326 t = GetPlayerColor(player_localnum) + 1;
333 case SPRITERULE_DEFAULT:
337 spriteimage = self.netname;
342 spriteimage = self.netname;
344 case SPRITERULE_TEAMPLAY:
345 if(t == NUM_SPECTATOR + 1)
346 spriteimage = self.netname3;
347 else if(self.team == t)
348 spriteimage = self.netname2;
350 spriteimage = self.netname;
353 error("Invalid waypointsprite rule!");
357 if(spriteimage == "")
360 ++waypointsprite_newcount;
363 dist = vlen(self.origin - view_origin);
366 a = self.alpha * autocvar_hud_panel_fg_alpha;
368 if(self.maxdistance > waypointsprite_normdistance)
369 a *= pow(bound(0, (self.maxdistance - dist) / (self.maxdistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent);
370 else if(self.maxdistance > 0)
371 a *= pow(bound(0, (waypointsprite_fadedistance - dist) / (waypointsprite_fadedistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent) * (1 - waypointsprite_minalpha) + waypointsprite_minalpha;
374 rgb = self.teamradar_color;
375 rgb = spritelookupcolor(spriteimage, rgb);
378 self.teamradar_color = '1 0 1';
379 printf("WARNING: sprite of name %s has no color, using pink so you notice it\n", spriteimage);
382 if(time - floor(time) > 0.5)
384 if(self.helpme && time < self.helpme)
385 a *= SPRITE_HELPME_BLINK;
387 a *= spritelookupblinkvalue(spriteimage);
399 rgb = fixrgbexcess(rgb);
404 o = project_3d_to_2d(self.origin);
406 || o.x < (vid_conwidth * waypointsprite_edgeoffset_left)
407 || o.y < (vid_conheight * waypointsprite_edgeoffset_top)
408 || o.x > (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right))
409 || o.y > (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom)))
411 // scale it to be just in view
415 d = o - '0.5 0 0' * vid_conwidth - '0 0.5 0' * vid_conheight;
416 ang = atan2(-d.x, -d.y);
420 f1 = d.x / vid_conwidth;
421 f2 = d.y / vid_conheight;
423 if(max(f1, -f1) > max(f2, -f2))
428 d = d * ((0.5 - waypointsprite_edgeoffset_right) / f1);
433 d = d * (-(0.5 - waypointsprite_edgeoffset_left) / f1);
441 d = d * ((0.5 - waypointsprite_edgeoffset_bottom) / f2);
446 d = d * (-(0.5 - waypointsprite_edgeoffset_top) / f2);
450 o = d + '0.5 0 0' * vid_conwidth + '0 0.5 0' * vid_conheight;
458 d = o - '0.5 0 0' * vid_conwidth - '0 0.5 0' * vid_conheight;
459 ang = atan2(-d.x, -d.y);
464 float edgedistance_min, crosshairdistance;
465 edgedistance_min = min((o.y - (vid_conheight * waypointsprite_edgeoffset_top)),
466 (o.x - (vid_conwidth * waypointsprite_edgeoffset_left)),
467 (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right)) - o.x,
468 (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom)) - o.y);
471 vidscale = max(vid_conwidth / vid_width, vid_conheight / vid_height);
473 crosshairdistance = sqrt( pow(o.x - vid_conwidth/2, 2) + pow(o.y - vid_conheight/2, 2) );
475 t = waypointsprite_scale * vidscale;
476 a *= waypointsprite_alpha;
479 a = a * (1 - (1 - waypointsprite_distancefadealpha) * (bound(0, dist/waypointsprite_distancefadedistance, 1)));
480 t = t * (1 - (1 - waypointsprite_distancefadescale) * (bound(0, dist/waypointsprite_distancefadedistance, 1)));
482 if (edgedistance_min < waypointsprite_edgefadedistance) {
483 a = a * (1 - (1 - waypointsprite_edgefadealpha) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1)));
484 t = t * (1 - (1 - waypointsprite_edgefadescale) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1)));
486 if(crosshairdistance < waypointsprite_crosshairfadedistance) {
487 a = a * (1 - (1 - waypointsprite_crosshairfadealpha) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1)));
488 t = t * (1 - (1 - waypointsprite_crosshairfadescale) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1)));
491 if(self.build_finished)
493 if(time < self.build_finished + 0.25)
495 if(time < self.build_started)
496 self.health = self.build_starthealth;
497 else if(time < self.build_finished)
498 self.health = (time - self.build_started) / (self.build_finished - self.build_started) * (1 - self.build_starthealth) + self.build_starthealth;
506 o = drawspritearrow(o, ang, rgb, a, SPRITE_ARROW_SCALE * t);
509 if(autocvar_g_waypointsprite_spam && waypointsprite_count >= autocvar_g_waypointsprite_spam)
512 txt = spritelookuptext(spriteimage);
513 if(self.helpme && time < self.helpme)
514 txt = sprintf(_("%s needing help!"), txt);
515 if(autocvar_g_waypointsprite_uppercase)
516 txt = strtoupper(txt);
518 draw_beginBoldFont();
521 o = drawspritetext(o, ang, (SPRITE_HEALTHBAR_WIDTH + 2 * SPRITE_HEALTHBAR_BORDER) * t, rgb, a, waypointsprite_fontsize * '1 1 0', txt);
524 if(self.build_finished)
529 marg = -(SPRITE_HEALTHBAR_MARGIN + SPRITE_HEALTHBAR_HEIGHT + 2 * SPRITE_HEALTHBAR_BORDER) * t - 0.5 * waypointsprite_fontsize;
531 marg = SPRITE_HEALTHBAR_MARGIN * t + 0.5 * waypointsprite_fontsize;
538 SPRITE_HEALTHBAR_WIDTH * t,
539 SPRITE_HEALTHBAR_HEIGHT * t,
541 SPRITE_HEALTHBAR_BORDER * t,
544 a * SPRITE_HEALTHBAR_BORDERALPHA,
546 a * SPRITE_HEALTHBAR_HEALTHALPHA,
552 o = drawspritetext(o, ang, 0, rgb, a, waypointsprite_fontsize * '1 1 0', txt);
557 void Ent_RemoveWaypointSprite()
560 strunzone(self.netname);
562 strunzone(self.netname2);
564 strunzone(self.netname3);
567 void Ent_WaypointSprite()
570 sendflags = ReadByte();
573 self.spawntime = time;
575 self.draw2d = Draw_WaypointSprite;
577 InterpolateOrigin_Undo();
578 self.iflags |= IFLAG_ORIGIN;
585 self.health = t / 191.0;
586 self.build_finished = 0;
590 t = (t - 192) * 256 + ReadByte();
591 self.build_started = servertime;
592 if(self.build_finished)
593 self.build_starthealth = bound(0, self.health, 1);
595 self.build_starthealth = 0;
596 self.build_finished = servertime + t / 32;
602 self.build_finished = 0;
607 // unfortunately, this needs to be exact (for the 3D display)
608 self.origin_x = ReadCoord();
609 self.origin_y = ReadCoord();
610 self.origin_z = ReadCoord();
611 setorigin(self, self.origin);
616 self.team = ReadByte();
617 self.rule = ReadByte();
623 strunzone(self.netname);
624 self.netname = strzone(ReadString());
630 strunzone(self.netname2);
631 self.netname2 = strzone(ReadString());
637 strunzone(self.netname3);
638 self.netname3 = strzone(ReadString());
643 self.lifetime = ReadCoord();
644 self.fadetime = ReadCoord();
645 self.maxdistance = ReadShort();
646 self.hideflags = ReadByte();
652 self.teamradar_icon = (f & 0x7F);
655 self.(teamradar_times[self.teamradar_time_index]) = time;
656 self.teamradar_time_index = (self.teamradar_time_index + 1) % MAX_TEAMRADAR_TIMES;
658 self.teamradar_color_x = ReadByte() / 255.0;
659 self.teamradar_color_y = ReadByte() / 255.0;
660 self.teamradar_color_z = ReadByte() / 255.0;
661 self.helpme = ReadByte() * 0.1;
663 self.helpme += servertime;
666 InterpolateOrigin_Note();
668 self.entremove = Ent_RemoveWaypointSprite;
671 void WaypointSprite_Load_Frames(string ext)
673 float dh, n, i, o, f;
674 string s, sname, sframes;
675 dh = search_begin(strcat("models/sprites/*_frame*", ext), false, false);
678 float ext_len = strlen(ext);
679 n = search_getsize(dh);
680 for(i = 0; i < n; ++i)
682 s = search_getfilename(dh, i);
683 s = substring(s, 15, strlen(s) - 15 - ext_len); // strip models/sprites/ and extension
685 o = strstrofs(s, "_frame", 0);
686 sname = strcat("/spriteframes/", substring(s, 0, o));
687 sframes = substring(s, o + 6, strlen(s) - o - 6);
688 f = stof(sframes) + 1;
689 db_put(tempdb, sname, ftos(max(f, stof(db_get(tempdb, sname)))));
694 void WaypointSprite_Load()
696 waypointsprite_fadedistance = vlen(mi_scale);
697 waypointsprite_normdistance = autocvar_g_waypointsprite_normdistance;
698 waypointsprite_minscale = autocvar_g_waypointsprite_minscale;
699 waypointsprite_minalpha = autocvar_g_waypointsprite_minalpha;
700 waypointsprite_distancealphaexponent = autocvar_g_waypointsprite_distancealphaexponent;
701 waypointsprite_timealphaexponent = autocvar_g_waypointsprite_timealphaexponent;
702 waypointsprite_scale = autocvar_g_waypointsprite_scale;
703 waypointsprite_fontsize = autocvar_g_waypointsprite_fontsize;
704 waypointsprite_edgefadealpha = autocvar_g_waypointsprite_edgefadealpha;
705 waypointsprite_edgefadescale = autocvar_g_waypointsprite_edgefadescale;
706 waypointsprite_edgefadedistance = autocvar_g_waypointsprite_edgefadedistance;
707 waypointsprite_edgeoffset_bottom = autocvar_g_waypointsprite_edgeoffset_bottom;
708 waypointsprite_edgeoffset_left = autocvar_g_waypointsprite_edgeoffset_left;
709 waypointsprite_edgeoffset_right = autocvar_g_waypointsprite_edgeoffset_right;
710 waypointsprite_edgeoffset_top = autocvar_g_waypointsprite_edgeoffset_top;
711 waypointsprite_crosshairfadealpha = autocvar_g_waypointsprite_crosshairfadealpha;
712 waypointsprite_crosshairfadescale = autocvar_g_waypointsprite_crosshairfadescale;
713 waypointsprite_crosshairfadedistance = autocvar_g_waypointsprite_crosshairfadedistance;
714 waypointsprite_distancefadealpha = autocvar_g_waypointsprite_distancefadealpha;
715 waypointsprite_distancefadescale = autocvar_g_waypointsprite_distancefadescale;
716 waypointsprite_distancefadedistance = waypointsprite_fadedistance * autocvar_g_waypointsprite_distancefadedistancemultiplier;
717 waypointsprite_alpha = autocvar_g_waypointsprite_alpha * (1 - autocvar__menu_alpha);
719 if(!waypointsprite_initialized)
721 WaypointSprite_Load_Frames(".tga");
722 WaypointSprite_Load_Frames(".jpg");
723 waypointsprite_initialized = true;
726 waypointsprite_count = waypointsprite_newcount;
727 waypointsprite_newcount = 0;