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 height, 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 + height) + ri * (sz.x - owidth) * 0.5;
77 drawquad(o - up * border, ri * owidth, up * border, "", rgb, a, f);
78 drawquad(o + up * height, ri * owidth, up * border, "", rgb, a, f);
79 drawquad(o, ri * border, up * height, "", rgb, a, f);
80 drawquad(o + ri * (owidth - border), ri * border, up * height, "", rgb, a, f);
81 drawquad(o + ri * (border + align * ((1 - h) * width)), ri * width * h, up * height, "", 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-neut": return 2;
172 case "ons-cp-atck-red": return 2;
173 case "ons-cp-atck-blue": return 2;
174 case "ons-cp-dfnd-red": return 0.5;
175 case "ons-cp-dfnd-blue": return 0.5;
176 case "item-invis": return 2;
177 case "item-extralife": return 2;
178 case "item-speed": return 2;
179 case "item-strength": return 2;
180 case "item-shield": return 2;
181 case "item-fuelregen": return 2;
182 case "item-jetpack": return 2;
183 case "tagged-target": return 2;
187 vector spritelookupcolor(string s, vector def)
189 if(substring(s, 0, 4) == "wpn-")
190 return (get_weaponinfo(stof(substring(s, 4, strlen(s)))).wpcolor);
194 case "keycarrier-friend": return '0 1 0';
198 string spritelookuptext(string s)
200 if(substring(s, 0, 4) == "wpn-") { return (get_weaponinfo(stof(substring(s, 4, strlen(s)))).message); }
201 if(substring(s, 0, 5) == "buff-") { return Buff_PrettyName(Buff_Type_FromSprite(s)); }
205 case "as-push": return _("Push");
206 case "as-destroy": return _("Destroy");
207 case "as-defend": return _("Defend");
208 case "bluebase": return _("Blue base");
209 case "danger": return _("DANGER");
210 case "enemyflagcarrier": return _("Enemy carrier");
211 case "flagcarrier": return _("Flag carrier");
212 case "flagdropped": return _("Dropped flag");
213 case "helpme": return _("Help me!");
214 case "here": return _("Here");
215 case "key-dropped": return _("Dropped key");
216 case "keycarrier-blue": return _("Key carrier");
217 case "keycarrier-finish": return _("Run here");
218 case "keycarrier-friend": return _("Key carrier");
219 case "keycarrier-pink": return _("Key carrier");
220 case "keycarrier-red": return _("Key carrier");
221 case "keycarrier-yellow": return _("Key carrier");
222 case "redbase": return _("Red base");
223 case "yellowbase": return _("Yellow base");
224 case "neutralbase": return _("White base");
225 case "pinkbase": return _("Pink base");
226 case "waypoint": return _("Waypoint");
227 case "ons-gen-red": return _("Generator");
228 case "ons-gen-blue": return _("Generator");
229 case "ons-gen-shielded": return _("Generator");
230 case "ons-cp-neut": return _("Control point");
231 case "ons-cp-red": return _("Control point");
232 case "ons-cp-blue": return _("Control point");
233 case "ons-cp-atck-neut": return _("Control point");
234 case "ons-cp-atck-red": return _("Control point");
235 case "ons-cp-atck-blue": return _("Control point");
236 case "ons-cp-dfnd-red": return _("Control point");
237 case "ons-cp-dfnd-blue": return _("Control point");
238 case "race-checkpoint": return _("Checkpoint");
239 case "race-finish": return _("Finish");
240 case "race-start": return _("Start");
241 case "race-start-finish": return (race_checkpointtime || race_mycheckpointtime) ? _("Finish") : _("Start");
242 case "goal": return _("Goal");
243 case "nb-ball": return _("Ball");
244 case "ka-ball": return _("Ball");
245 case "ka-ballcarrier": return _("Ball carrier");
246 case "dom-neut": return _("Control point");
247 case "dom-red": return _("Control point");
248 case "dom-blue": return _("Control point");
249 case "dom-yellow": return _("Control point");
250 case "dom-pink": return _("Control point");
251 case "item-invis": return _("Invisibility");
252 case "item-extralife": return _("Extra life");
253 case "item-speed": return _("Speed");
254 case "item-strength": return _("Strength");
255 case "item-shield": return _("Shield");
256 case "item-fuelregen": return _("Fuel regen");
257 case "item-jetpack": return _("Jet Pack");
258 case "frozen": return _("Frozen!");
259 case "tagged-target": return _("Tagged");
260 case "vehicle": return _("Vehicle");
265 vector fixrgbexcess_move(vector rgb, vector src, vector dst)
267 vector yvec = '0.299 0.587 0.114';
268 return rgb + dst * ((src * yvec) / (dst * yvec)) * ((rgb - '1 1 1') * src);
270 vector fixrgbexcess(vector rgb)
274 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 1 1');
277 rgb = fixrgbexcess_move(rgb, '0 1 0', '0 0 1');
283 rgb = fixrgbexcess_move(rgb, '0 0 1', '0 1 0');
290 rgb = fixrgbexcess_move(rgb, '0 1 0', '1 0 1');
293 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 0 1');
299 rgb = fixrgbexcess_move(rgb, '0 0 1', '1 0 0');
306 rgb = fixrgbexcess_move(rgb, '0 0 1', '1 1 0');
309 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 1 0');
315 rgb = fixrgbexcess_move(rgb, '0 1 0', '1 0 0');
323 void Draw_WaypointSprite()
329 self.alpha = pow(bound(0, (self.fadetime - time) / self.lifetime, 1), waypointsprite_timealphaexponent);
333 if(self.hideflags & 2)
334 return; // radar only
336 if(autocvar_cl_hidewaypoints >= 2)
339 if(self.hideflags & 1)
340 if(autocvar_cl_hidewaypoints)
341 return; // fixed waypoint
343 InterpolateOrigin_Do();
345 t = GetPlayerColor(player_localnum) + 1;
352 case SPRITERULE_DEFAULT:
356 spriteimage = self.netname;
361 spriteimage = self.netname;
363 case SPRITERULE_TEAMPLAY:
364 if(t == NUM_SPECTATOR + 1)
365 spriteimage = self.netname3;
366 else if(self.team == t)
367 spriteimage = self.netname2;
369 spriteimage = self.netname;
372 error("Invalid waypointsprite rule!");
376 if(spriteimage == "")
379 ++waypointsprite_newcount;
382 dist = vlen(self.origin - view_origin);
385 a = self.alpha * autocvar_hud_panel_fg_alpha;
387 if(self.maxdistance > waypointsprite_normdistance)
388 a *= pow(bound(0, (self.maxdistance - dist) / (self.maxdistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent);
389 else if(self.maxdistance > 0)
390 a *= pow(bound(0, (waypointsprite_fadedistance - dist) / (waypointsprite_fadedistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent) * (1 - waypointsprite_minalpha) + waypointsprite_minalpha;
393 rgb = self.teamradar_color;
394 rgb = spritelookupcolor(spriteimage, rgb);
397 self.teamradar_color = '1 0 1';
398 printf("WARNING: sprite of name %s has no color, using pink so you notice it\n", spriteimage);
401 if(time - floor(time) > 0.5)
403 if(self.helpme && time < self.helpme)
404 a *= SPRITE_HELPME_BLINK;
406 a *= spritelookupblinkvalue(spriteimage);
418 rgb = fixrgbexcess(rgb);
423 o = project_3d_to_2d(self.origin);
425 || o.x < (vid_conwidth * waypointsprite_edgeoffset_left)
426 || o.y < (vid_conheight * waypointsprite_edgeoffset_top)
427 || o.x > (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right))
428 || o.y > (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom)))
430 // scale it to be just in view
434 d = o - '0.5 0 0' * vid_conwidth - '0 0.5 0' * vid_conheight;
435 ang = atan2(-d.x, -d.y);
439 f1 = d.x / vid_conwidth;
440 f2 = d.y / vid_conheight;
442 if(max(f1, -f1) > max(f2, -f2))
447 d = d * ((0.5 - waypointsprite_edgeoffset_right) / f1);
452 d = d * (-(0.5 - waypointsprite_edgeoffset_left) / f1);
460 d = d * ((0.5 - waypointsprite_edgeoffset_bottom) / f2);
465 d = d * (-(0.5 - waypointsprite_edgeoffset_top) / f2);
469 o = d + '0.5 0 0' * vid_conwidth + '0 0.5 0' * vid_conheight;
477 d = o - '0.5 0 0' * vid_conwidth - '0 0.5 0' * vid_conheight;
478 ang = atan2(-d.x, -d.y);
483 float edgedistance_min, crosshairdistance;
484 edgedistance_min = min((o.y - (vid_conheight * waypointsprite_edgeoffset_top)),
485 (o.x - (vid_conwidth * waypointsprite_edgeoffset_left)),
486 (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right)) - o.x,
487 (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom)) - o.y);
490 vidscale = max(vid_conwidth / vid_width, vid_conheight / vid_height);
492 crosshairdistance = sqrt( pow(o.x - vid_conwidth/2, 2) + pow(o.y - vid_conheight/2, 2) );
494 t = waypointsprite_scale * vidscale;
495 a *= waypointsprite_alpha;
498 a = a * (1 - (1 - waypointsprite_distancefadealpha) * (bound(0, dist/waypointsprite_distancefadedistance, 1)));
499 t = t * (1 - (1 - waypointsprite_distancefadescale) * (bound(0, dist/waypointsprite_distancefadedistance, 1)));
501 if (edgedistance_min < waypointsprite_edgefadedistance) {
502 a = a * (1 - (1 - waypointsprite_edgefadealpha) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1)));
503 t = t * (1 - (1 - waypointsprite_edgefadescale) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1)));
505 if(crosshairdistance < waypointsprite_crosshairfadedistance) {
506 a = a * (1 - (1 - waypointsprite_crosshairfadealpha) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1)));
507 t = t * (1 - (1 - waypointsprite_crosshairfadescale) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1)));
510 if(self.build_finished)
512 if(time < self.build_finished + 0.25)
514 if(time < self.build_started)
515 self.health = self.build_starthealth;
516 else if(time < self.build_finished)
517 self.health = (time - self.build_started) / (self.build_finished - self.build_started) * (1 - self.build_starthealth) + self.build_starthealth;
525 o = drawspritearrow(o, ang, rgb, a, SPRITE_ARROW_SCALE * t);
528 if(autocvar_g_waypointsprite_spam && waypointsprite_count >= autocvar_g_waypointsprite_spam)
531 txt = spritelookuptext(spriteimage);
532 if(self.helpme && time < self.helpme)
533 txt = sprintf(_("%s needing help!"), txt);
534 if(autocvar_g_waypointsprite_uppercase)
535 txt = strtoupper(txt);
537 draw_beginBoldFont();
540 o = drawspritetext(o, ang, (SPRITE_HEALTHBAR_WIDTH + 2 * SPRITE_HEALTHBAR_BORDER) * t, rgb, a, waypointsprite_fontsize * '1 1 0', txt);
543 if(self.build_finished)
548 marg = -(SPRITE_HEALTHBAR_MARGIN + SPRITE_HEALTHBAR_HEIGHT + 2 * SPRITE_HEALTHBAR_BORDER) * t - 0.5 * waypointsprite_fontsize;
550 marg = SPRITE_HEALTHBAR_MARGIN * t + 0.5 * waypointsprite_fontsize;
557 SPRITE_HEALTHBAR_WIDTH * t,
558 SPRITE_HEALTHBAR_HEIGHT * t,
560 SPRITE_HEALTHBAR_BORDER * t,
563 a * SPRITE_HEALTHBAR_BORDERALPHA,
565 a * SPRITE_HEALTHBAR_HEALTHALPHA,
571 o = drawspritetext(o, ang, 0, rgb, a, waypointsprite_fontsize * '1 1 0', txt);
576 void Ent_RemoveWaypointSprite()
579 strunzone(self.netname);
581 strunzone(self.netname2);
583 strunzone(self.netname3);
586 void Ent_WaypointSprite()
589 sendflags = ReadByte();
592 self.spawntime = time;
594 self.draw2d = Draw_WaypointSprite;
596 InterpolateOrigin_Undo();
597 self.iflags |= IFLAG_ORIGIN;
604 self.health = t / 191.0;
605 self.build_finished = 0;
609 t = (t - 192) * 256 + ReadByte();
610 self.build_started = servertime;
611 if(self.build_finished)
612 self.build_starthealth = bound(0, self.health, 1);
614 self.build_starthealth = 0;
615 self.build_finished = servertime + t / 32;
621 self.build_finished = 0;
626 // unfortunately, this needs to be exact (for the 3D display)
627 self.origin_x = ReadCoord();
628 self.origin_y = ReadCoord();
629 self.origin_z = ReadCoord();
630 setorigin(self, self.origin);
635 self.team = ReadByte();
636 self.rule = ReadByte();
642 strunzone(self.netname);
643 self.netname = strzone(ReadString());
649 strunzone(self.netname2);
650 self.netname2 = strzone(ReadString());
656 strunzone(self.netname3);
657 self.netname3 = strzone(ReadString());
662 self.lifetime = ReadCoord();
663 self.fadetime = ReadCoord();
664 self.maxdistance = ReadShort();
665 self.hideflags = ReadByte();
671 self.teamradar_icon = (f & 0x7F);
674 self.(teamradar_times[self.teamradar_time_index]) = time;
675 self.teamradar_time_index = (self.teamradar_time_index + 1) % MAX_TEAMRADAR_TIMES;
677 self.teamradar_color_x = ReadByte() / 255.0;
678 self.teamradar_color_y = ReadByte() / 255.0;
679 self.teamradar_color_z = ReadByte() / 255.0;
680 self.helpme = ReadByte() * 0.1;
682 self.helpme += servertime;
685 InterpolateOrigin_Note();
687 self.entremove = Ent_RemoveWaypointSprite;
690 void WaypointSprite_Load_Frames(string ext)
692 float dh, n, i, o, f;
693 string s, sname, sframes;
694 dh = search_begin(strcat("models/sprites/*_frame*", ext), false, false);
697 float ext_len = strlen(ext);
698 n = search_getsize(dh);
699 for(i = 0; i < n; ++i)
701 s = search_getfilename(dh, i);
702 s = substring(s, 15, strlen(s) - 15 - ext_len); // strip models/sprites/ and extension
704 o = strstrofs(s, "_frame", 0);
705 sname = strcat("/spriteframes/", substring(s, 0, o));
706 sframes = substring(s, o + 6, strlen(s) - o - 6);
707 f = stof(sframes) + 1;
708 db_put(tempdb, sname, ftos(max(f, stof(db_get(tempdb, sname)))));
713 void WaypointSprite_Load()
715 waypointsprite_fadedistance = vlen(mi_scale);
716 waypointsprite_normdistance = autocvar_g_waypointsprite_normdistance;
717 waypointsprite_minscale = autocvar_g_waypointsprite_minscale;
718 waypointsprite_minalpha = autocvar_g_waypointsprite_minalpha;
719 waypointsprite_distancealphaexponent = autocvar_g_waypointsprite_distancealphaexponent;
720 waypointsprite_timealphaexponent = autocvar_g_waypointsprite_timealphaexponent;
721 waypointsprite_scale = autocvar_g_waypointsprite_scale;
722 waypointsprite_fontsize = autocvar_g_waypointsprite_fontsize;
723 waypointsprite_edgefadealpha = autocvar_g_waypointsprite_edgefadealpha;
724 waypointsprite_edgefadescale = autocvar_g_waypointsprite_edgefadescale;
725 waypointsprite_edgefadedistance = autocvar_g_waypointsprite_edgefadedistance;
726 waypointsprite_edgeoffset_bottom = autocvar_g_waypointsprite_edgeoffset_bottom;
727 waypointsprite_edgeoffset_left = autocvar_g_waypointsprite_edgeoffset_left;
728 waypointsprite_edgeoffset_right = autocvar_g_waypointsprite_edgeoffset_right;
729 waypointsprite_edgeoffset_top = autocvar_g_waypointsprite_edgeoffset_top;
730 waypointsprite_crosshairfadealpha = autocvar_g_waypointsprite_crosshairfadealpha;
731 waypointsprite_crosshairfadescale = autocvar_g_waypointsprite_crosshairfadescale;
732 waypointsprite_crosshairfadedistance = autocvar_g_waypointsprite_crosshairfadedistance;
733 waypointsprite_distancefadealpha = autocvar_g_waypointsprite_distancefadealpha;
734 waypointsprite_distancefadescale = autocvar_g_waypointsprite_distancefadescale;
735 waypointsprite_distancefadedistance = waypointsprite_fadedistance * autocvar_g_waypointsprite_distancefadedistancemultiplier;
736 waypointsprite_alpha = autocvar_g_waypointsprite_alpha * (1 - autocvar__menu_alpha);
738 if(!waypointsprite_initialized)
740 WaypointSprite_Load_Frames(".tga");
741 WaypointSprite_Load_Frames(".jpg");
742 waypointsprite_initialized = true;
745 waypointsprite_count = waypointsprite_newcount;
746 waypointsprite_newcount = 0;