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, 0, BUFFS_COUNT,
217 return buff.m_prettyName;
222 case "as-push": return _("Push");
223 case "as-destroy": return _("Destroy");
224 case "as-defend": return _("Defend");
225 case "bluebase": return _("Blue base");
226 case "danger": return _("DANGER");
227 case "enemyflagcarrier": return _("Enemy carrier");
228 case "flagcarrier": return _("Flag carrier");
229 case "flagdropped": return _("Dropped flag");
230 case "helpme": return _("Help me!");
231 case "here": return _("Here");
232 case "key-dropped": return _("Dropped key");
233 case "keycarrier-blue": return _("Key carrier");
234 case "keycarrier-finish": return _("Run here");
235 case "keycarrier-friend": return _("Key carrier");
236 case "keycarrier-pink": return _("Key carrier");
237 case "keycarrier-red": return _("Key carrier");
238 case "keycarrier-yellow": return _("Key carrier");
239 case "redbase": return _("Red base");
240 case "yellowbase": return _("Yellow base");
241 case "neutralbase": return _("White base");
242 case "pinkbase": return _("Pink base");
243 case "waypoint": return _("Waypoint");
244 case "ons-gen-red": return _("Generator");
245 case "ons-gen-blue": return _("Generator");
246 case "ons-gen-shielded": return _("Generator");
247 case "ons-cp-neut": return _("Control point");
248 case "ons-cp-red": return _("Control point");
249 case "ons-cp-blue": return _("Control point");
250 case "ons-cp-atck-neut": return _("Control point");
251 case "ons-cp-atck-red": return _("Control point");
252 case "ons-cp-atck-blue": return _("Control point");
253 case "ons-cp-dfnd-red": return _("Control point");
254 case "ons-cp-dfnd-blue": return _("Control point");
255 case "race-checkpoint": return _("Checkpoint");
256 case "race-finish": return _("Finish");
257 case "race-start": return _("Start");
258 case "race-start-finish": return (race_checkpointtime || race_mycheckpointtime) ? _("Finish") : _("Start");
259 case "goal": return _("Goal");
260 case "nb-ball": return _("Ball");
261 case "ka-ball": return _("Ball");
262 case "ka-ballcarrier": return _("Ball carrier");
263 case "dom-neut": return _("Control point");
264 case "dom-red": return _("Control point");
265 case "dom-blue": return _("Control point");
266 case "dom-yellow": return _("Control point");
267 case "dom-pink": return _("Control point");
268 case "item_health_mega": return _("Mega health");
269 case "item_armor_large": return _("Large armor");
270 case "item-invis": return _("Invisibility");
271 case "item-extralife": return _("Extra life");
272 case "item-speed": return _("Speed");
273 case "item-strength": return _("Strength");
274 case "item-shield": return _("Shield");
275 case "item-fuelregen": return _("Fuel regen");
276 case "item-jetpack": return _("Jet Pack");
277 case "frozen": return _("Frozen!");
278 case "tagged-target": return _("Tagged");
279 case "vehicle": return _("Vehicle");
284 vector fixrgbexcess_move(vector rgb, vector src, vector dst)
286 vector yvec = '0.299 0.587 0.114';
287 return rgb + dst * ((src * yvec) / (dst * yvec)) * ((rgb - '1 1 1') * src);
289 vector fixrgbexcess(vector rgb)
293 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 1 1');
296 rgb = fixrgbexcess_move(rgb, '0 1 0', '0 0 1');
302 rgb = fixrgbexcess_move(rgb, '0 0 1', '0 1 0');
309 rgb = fixrgbexcess_move(rgb, '0 1 0', '1 0 1');
312 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 0 1');
318 rgb = fixrgbexcess_move(rgb, '0 0 1', '1 0 0');
325 rgb = fixrgbexcess_move(rgb, '0 0 1', '1 1 0');
328 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 1 0');
334 rgb = fixrgbexcess_move(rgb, '0 1 0', '1 0 0');
342 void Draw_WaypointSprite()
348 self.alpha = pow(bound(0, (self.fadetime - time) / self.lifetime, 1), waypointsprite_timealphaexponent);
352 if(self.hideflags & 2)
353 return; // radar only
355 if(autocvar_cl_hidewaypoints >= 2)
358 if(self.hideflags & 1)
359 if(autocvar_cl_hidewaypoints)
360 return; // fixed waypoint
362 InterpolateOrigin_Do();
364 t = GetPlayerColor(player_localnum) + 1;
371 case SPRITERULE_SPECTATOR:
373 (autocvar_g_waypointsprite_itemstime == 1 && t == NUM_SPECTATOR + 1)
374 || (autocvar_g_waypointsprite_itemstime == 2 && (t == NUM_SPECTATOR + 1 || warmup_stage))
377 spriteimage = self.netname;
379 case SPRITERULE_DEFAULT:
383 spriteimage = self.netname;
388 spriteimage = self.netname;
390 case SPRITERULE_TEAMPLAY:
391 if(t == NUM_SPECTATOR + 1)
392 spriteimage = self.netname3;
393 else if(self.team == t)
394 spriteimage = self.netname2;
396 spriteimage = self.netname;
399 error("Invalid waypointsprite rule!");
403 if(spriteimage == "")
406 ++waypointsprite_newcount;
409 dist = vlen(self.origin - view_origin);
412 a = self.alpha * autocvar_hud_panel_fg_alpha;
414 if(self.maxdistance > waypointsprite_normdistance)
415 a *= pow(bound(0, (self.maxdistance - dist) / (self.maxdistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent);
416 else if(self.maxdistance > 0)
417 a *= pow(bound(0, (waypointsprite_fadedistance - dist) / (waypointsprite_fadedistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent) * (1 - waypointsprite_minalpha) + waypointsprite_minalpha;
420 rgb = self.teamradar_color;
421 rgb = spritelookupcolor(spriteimage, rgb);
424 self.teamradar_color = '1 0 1';
425 printf("WARNING: sprite of name %s has no color, using pink so you notice it\n", spriteimage);
428 if(time - floor(time) > 0.5)
430 if(self.helpme && time < self.helpme)
431 a *= SPRITE_HELPME_BLINK;
432 else if(!self.lifetime) // fading out waypoints don't blink
433 a *= spritelookupblinkvalue(spriteimage);
445 rgb = fixrgbexcess(rgb);
450 o = project_3d_to_2d(self.origin);
452 || o.x < (vid_conwidth * waypointsprite_edgeoffset_left)
453 || o.y < (vid_conheight * waypointsprite_edgeoffset_top)
454 || o.x > (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right))
455 || o.y > (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom)))
457 // scale it to be just in view
461 d = o - '0.5 0 0' * vid_conwidth - '0 0.5 0' * vid_conheight;
462 ang = atan2(-d.x, -d.y);
466 f1 = d.x / vid_conwidth;
467 f2 = d.y / vid_conheight;
469 if(max(f1, -f1) > max(f2, -f2))
474 d = d * ((0.5 - waypointsprite_edgeoffset_right) / f1);
479 d = d * (-(0.5 - waypointsprite_edgeoffset_left) / f1);
487 d = d * ((0.5 - waypointsprite_edgeoffset_bottom) / f2);
492 d = d * (-(0.5 - waypointsprite_edgeoffset_top) / f2);
496 o = d + '0.5 0 0' * vid_conwidth + '0 0.5 0' * vid_conheight;
504 d = o - '0.5 0 0' * vid_conwidth - '0 0.5 0' * vid_conheight;
505 ang = atan2(-d.x, -d.y);
510 float edgedistance_min, crosshairdistance;
511 edgedistance_min = min((o.y - (vid_conheight * waypointsprite_edgeoffset_top)),
512 (o.x - (vid_conwidth * waypointsprite_edgeoffset_left)),
513 (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right)) - o.x,
514 (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom)) - o.y);
517 vidscale = max(vid_conwidth / vid_width, vid_conheight / vid_height);
519 crosshairdistance = sqrt( pow(o.x - vid_conwidth/2, 2) + pow(o.y - vid_conheight/2, 2) );
521 t = waypointsprite_scale * vidscale;
522 a *= waypointsprite_alpha;
525 a = a * (1 - (1 - waypointsprite_distancefadealpha) * (bound(0, dist/waypointsprite_distancefadedistance, 1)));
526 t = t * (1 - (1 - waypointsprite_distancefadescale) * (bound(0, dist/waypointsprite_distancefadedistance, 1)));
528 if (edgedistance_min < waypointsprite_edgefadedistance) {
529 a = a * (1 - (1 - waypointsprite_edgefadealpha) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1)));
530 t = t * (1 - (1 - waypointsprite_edgefadescale) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1)));
532 if(crosshairdistance < waypointsprite_crosshairfadedistance) {
533 a = a * (1 - (1 - waypointsprite_crosshairfadealpha) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1)));
534 t = t * (1 - (1 - waypointsprite_crosshairfadescale) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1)));
537 if(self.build_finished)
539 if(time < self.build_finished + 0.25)
541 if(time < self.build_started)
542 self.health = self.build_starthealth;
543 else if(time < self.build_finished)
544 self.health = (time - self.build_started) / (self.build_finished - self.build_started) * (1 - self.build_starthealth) + self.build_starthealth;
552 o = drawspritearrow(o, ang, rgb, a, SPRITE_ARROW_SCALE * t);
555 if(autocvar_g_waypointsprite_spam && waypointsprite_count >= autocvar_g_waypointsprite_spam)
558 txt = spritelookuptext(spriteimage);
559 if(self.helpme && time < self.helpme)
560 txt = sprintf(_("%s needing help!"), txt);
561 if(autocvar_g_waypointsprite_uppercase)
562 txt = strtoupper(txt);
564 draw_beginBoldFont();
567 o = drawspritetext(o, ang, (SPRITE_HEALTHBAR_WIDTH + 2 * SPRITE_HEALTHBAR_BORDER) * t, rgb, a, waypointsprite_fontsize * '1 1 0', txt);
570 if(self.build_finished)
575 marg = -(SPRITE_HEALTHBAR_MARGIN + SPRITE_HEALTHBAR_HEIGHT + 2 * SPRITE_HEALTHBAR_BORDER) * t - 0.5 * waypointsprite_fontsize;
577 marg = SPRITE_HEALTHBAR_MARGIN * t + 0.5 * waypointsprite_fontsize;
584 SPRITE_HEALTHBAR_WIDTH * t,
585 SPRITE_HEALTHBAR_HEIGHT * t,
587 SPRITE_HEALTHBAR_BORDER * t,
590 a * SPRITE_HEALTHBAR_BORDERALPHA,
592 a * SPRITE_HEALTHBAR_HEALTHALPHA,
598 o = drawspritetext(o, ang, 0, rgb, a, waypointsprite_fontsize * '1 1 0', txt);
603 void Ent_RemoveWaypointSprite()
606 strunzone(self.netname);
608 strunzone(self.netname2);
610 strunzone(self.netname3);
613 void Ent_WaypointSprite()
616 sendflags = ReadByte();
619 self.spawntime = time;
621 self.draw2d = Draw_WaypointSprite;
623 InterpolateOrigin_Undo();
624 self.iflags |= IFLAG_ORIGIN;
631 self.health = t / 191.0;
632 self.build_finished = 0;
636 t = (t - 192) * 256 + ReadByte();
637 self.build_started = servertime;
638 if(self.build_finished)
639 self.build_starthealth = bound(0, self.health, 1);
641 self.build_starthealth = 0;
642 self.build_finished = servertime + t / 32;
648 self.build_finished = 0;
653 // unfortunately, this needs to be exact (for the 3D display)
654 self.origin_x = ReadCoord();
655 self.origin_y = ReadCoord();
656 self.origin_z = ReadCoord();
657 setorigin(self, self.origin);
662 self.team = ReadByte();
663 self.rule = ReadByte();
669 strunzone(self.netname);
670 self.netname = strzone(ReadString());
676 strunzone(self.netname2);
677 self.netname2 = strzone(ReadString());
683 strunzone(self.netname3);
684 self.netname3 = strzone(ReadString());
689 self.lifetime = ReadCoord();
690 self.fadetime = ReadCoord();
691 self.maxdistance = ReadShort();
692 self.hideflags = ReadByte();
698 self.teamradar_icon = (f & 0x7F);
701 self.(teamradar_times[self.teamradar_time_index]) = time;
702 self.teamradar_time_index = (self.teamradar_time_index + 1) % MAX_TEAMRADAR_TIMES;
704 self.teamradar_color_x = ReadByte() / 255.0;
705 self.teamradar_color_y = ReadByte() / 255.0;
706 self.teamradar_color_z = ReadByte() / 255.0;
707 self.helpme = ReadByte() * 0.1;
709 self.helpme += servertime;
712 InterpolateOrigin_Note();
714 self.entremove = Ent_RemoveWaypointSprite;
717 void WaypointSprite_Load_Frames(string ext)
719 float dh, n, i, o, f;
720 string s, sname, sframes;
721 dh = search_begin(strcat("models/sprites/*_frame*", ext), false, false);
724 float ext_len = strlen(ext);
725 n = search_getsize(dh);
726 for(i = 0; i < n; ++i)
728 s = search_getfilename(dh, i);
729 s = substring(s, 15, strlen(s) - 15 - ext_len); // strip models/sprites/ and extension
731 o = strstrofs(s, "_frame", 0);
732 sname = strcat("/spriteframes/", substring(s, 0, o));
733 sframes = substring(s, o + 6, strlen(s) - o - 6);
734 f = stof(sframes) + 1;
735 db_put(tempdb, sname, ftos(max(f, stof(db_get(tempdb, sname)))));
740 void WaypointSprite_Load()
742 waypointsprite_fadedistance = vlen(mi_scale);
743 waypointsprite_normdistance = autocvar_g_waypointsprite_normdistance;
744 waypointsprite_minscale = autocvar_g_waypointsprite_minscale;
745 waypointsprite_minalpha = autocvar_g_waypointsprite_minalpha;
746 waypointsprite_distancealphaexponent = autocvar_g_waypointsprite_distancealphaexponent;
747 waypointsprite_timealphaexponent = autocvar_g_waypointsprite_timealphaexponent;
748 waypointsprite_scale = autocvar_g_waypointsprite_scale;
749 waypointsprite_fontsize = autocvar_g_waypointsprite_fontsize;
750 waypointsprite_edgefadealpha = autocvar_g_waypointsprite_edgefadealpha;
751 waypointsprite_edgefadescale = autocvar_g_waypointsprite_edgefadescale;
752 waypointsprite_edgefadedistance = autocvar_g_waypointsprite_edgefadedistance;
753 waypointsprite_edgeoffset_bottom = autocvar_g_waypointsprite_edgeoffset_bottom;
754 waypointsprite_edgeoffset_left = autocvar_g_waypointsprite_edgeoffset_left;
755 waypointsprite_edgeoffset_right = autocvar_g_waypointsprite_edgeoffset_right;
756 waypointsprite_edgeoffset_top = autocvar_g_waypointsprite_edgeoffset_top;
757 waypointsprite_crosshairfadealpha = autocvar_g_waypointsprite_crosshairfadealpha;
758 waypointsprite_crosshairfadescale = autocvar_g_waypointsprite_crosshairfadescale;
759 waypointsprite_crosshairfadedistance = autocvar_g_waypointsprite_crosshairfadedistance;
760 waypointsprite_distancefadealpha = autocvar_g_waypointsprite_distancefadealpha;
761 waypointsprite_distancefadescale = autocvar_g_waypointsprite_distancefadescale;
762 waypointsprite_distancefadedistance = waypointsprite_fadedistance * autocvar_g_waypointsprite_distancefadedistancemultiplier;
763 waypointsprite_alpha = autocvar_g_waypointsprite_alpha * (1 - autocvar__menu_alpha);
765 if(!waypointsprite_initialized)
767 WaypointSprite_Load_Frames(".tga");
768 WaypointSprite_Load_Frames(".jpg");
769 waypointsprite_initialized = true;
772 waypointsprite_count = waypointsprite_newcount;
773 waypointsprite_newcount = 0;