1 #include "waypointsprites.qh"
3 REGISTER_MUTATOR(waypointsprites, true);
6 /** flags origin [team displayrule] [spritename] [spritename2] [spritename3] [lifetime maxdistance hideable] */
7 float WaypointSprite_SendEntity(entity to, float sendflags)
9 WriteMutator(MSG_ENTITY, waypointsprites);
11 sendflags = sendflags & 0x7F;
15 else if (self.max_health || (self.pain_finished && (time < self.pain_finished + 0.25)))
18 WriteByte(MSG_ENTITY, sendflags);
24 WriteByte(MSG_ENTITY, (self.health / self.max_health) * 191.0);
28 float dt = self.pain_finished - time;
29 dt = bound(0, dt * 32, 16383);
30 WriteByte(MSG_ENTITY, (dt & 0xFF00) / 256 + 192);
31 WriteByte(MSG_ENTITY, (dt & 0x00FF));
37 WriteCoord(MSG_ENTITY, self.origin.x);
38 WriteCoord(MSG_ENTITY, self.origin.y);
39 WriteCoord(MSG_ENTITY, self.origin.z);
44 WriteByte(MSG_ENTITY, self.team);
45 WriteByte(MSG_ENTITY, self.rule);
49 WriteString(MSG_ENTITY, self.model1);
52 WriteString(MSG_ENTITY, self.model2);
55 WriteString(MSG_ENTITY, self.model3);
59 WriteCoord(MSG_ENTITY, self.fade_time);
60 WriteCoord(MSG_ENTITY, self.teleport_time);
61 WriteShort(MSG_ENTITY, self.fade_rate); // maxdist
65 if (self.exteriormodeltoclient == to)
69 if (self.owner.classname == "onslaught_controlpoint")
71 entity wp_owner = self.owner;
72 entity e = WaypointSprite_getviewentity(to);
73 if (SAME_TEAM(e, wp_owner) && wp_owner.goalentity.health >= wp_owner.goalentity.max_health) { f |= 2; }
74 if (!ons_ControlPoint_Attackable(wp_owner, e.team)) { f |= 2; }
76 if (self.owner.classname == "onslaught_generator")
78 entity wp_owner = self.owner;
79 if (wp_owner.isshielded && wp_owner.health >= wp_owner.max_health) { f |= 2; }
80 if (wp_owner.health <= 0) { f |= 2; }
83 WriteByte(MSG_ENTITY, f);
88 WriteByte(MSG_ENTITY, self.cnt); // icon on radar
89 WriteByte(MSG_ENTITY, self.colormod.x * 255.0);
90 WriteByte(MSG_ENTITY, self.colormod.y * 255.0);
91 WriteByte(MSG_ENTITY, self.colormod.z * 255.0);
93 if (WaypointSprite_isteammate(self.owner, WaypointSprite_getviewentity(to)))
95 float dt = (self.waypointsprite_helpmetime - time) / 0.1;
100 WriteByte(MSG_ENTITY, dt);
103 WriteByte(MSG_ENTITY, 0);
111 void Ent_WaypointSprite();
112 MUTATOR_HOOKFUNCTION(waypointsprites, CSQC_Ent_Update) {
113 if (MUTATOR_RETURNVALUE) return false;
114 if (!ReadMutatorEquals(mutator_argv_int_0, waypointsprites)) return false;
115 Ent_WaypointSprite();
119 void Ent_RemoveWaypointSprite()
121 if (self.netname) strunzone(self.netname);
122 if (self.netname2) strunzone(self.netname2);
123 if (self.netname3) strunzone(self.netname3);
126 /** flags origin [team displayrule] [spritename] [spritename2] [spritename3] [lifetime maxdistance hideable] */
127 void Ent_WaypointSprite()
129 int sendflags = ReadByte();
132 self.spawntime = time;
134 self.draw2d = Draw_WaypointSprite;
136 InterpolateOrigin_Undo();
137 self.iflags |= IFLAG_ORIGIN;
139 if (sendflags & 0x80)
144 self.health = t / 191.0;
145 self.build_finished = 0;
149 t = (t - 192) * 256 + ReadByte();
150 self.build_started = servertime;
151 if (self.build_finished)
152 self.build_starthealth = bound(0, self.health, 1);
154 self.build_starthealth = 0;
155 self.build_finished = servertime + t / 32;
161 self.build_finished = 0;
166 // unfortunately, this needs to be exact (for the 3D display)
167 self.origin_x = ReadCoord();
168 self.origin_y = ReadCoord();
169 self.origin_z = ReadCoord();
170 setorigin(self, self.origin);
175 self.team = ReadByte();
176 self.rule = ReadByte();
182 strunzone(self.netname);
183 self.netname = strzone(ReadString());
189 strunzone(self.netname2);
190 self.netname2 = strzone(ReadString());
196 strunzone(self.netname3);
197 self.netname3 = strzone(ReadString());
202 self.lifetime = ReadCoord();
203 self.fadetime = ReadCoord();
204 self.maxdistance = ReadShort();
205 self.hideflags = ReadByte();
211 self.teamradar_icon = (f & 0x7F);
214 self.(teamradar_times[self.teamradar_time_index]) = time;
215 self.teamradar_time_index = (self.teamradar_time_index + 1) % MAX_TEAMRADAR_TIMES;
217 self.teamradar_color_x = ReadByte() / 255.0;
218 self.teamradar_color_y = ReadByte() / 255.0;
219 self.teamradar_color_z = ReadByte() / 255.0;
220 self.helpme = ReadByte() * 0.1;
222 self.helpme += servertime;
225 InterpolateOrigin_Note();
227 self.entremove = Ent_RemoveWaypointSprite;
232 float spritelookupblinkvalue(string s)
234 if (substring(s, 0, 4) == "wpn-")
235 if (get_weaponinfo(stof(substring(s, 4, strlen(s)))).spawnflags & WEP_FLAG_SUPERWEAPON)
238 FOREACH(ITEMS, it.m_waypoint == s, LAMBDA(
239 return it.m_waypointblink;
244 case "ons-cp-atck": return 2;
245 case "ons-cp-dfnd": return 0.5;
246 case "item-invis": return 2;
247 case "item-extralife": return 2;
248 case "item-speed": return 2;
249 case "tagged-target": return 2;
254 vector spritelookupcolor(string s, vector def)
256 if (substring(s, 0, 4) == "wpn-")
257 return (get_weaponinfo(stof(substring(s, 4, strlen(s)))).wpcolor);
261 case "keycarrier-friend": return '0 1 0';
266 string spritelookuptext(string s)
268 if (substring(s, 0, 4) == "wpn-") { return (get_weaponinfo(stof(substring(s, 4, strlen(s)))).message); }
269 if (substring(s, 0, 5) == "buff-")
271 entity buff = BUFF_NULL;
272 FOREACH(BUFFS, it.m_sprite == s, LAMBDA(
276 return buff.m_prettyName;
281 case "as-push": return _("Push");
282 case "as-destroy": return _("Destroy");
283 case "as-defend": return _("Defend");
284 case "bluebase": return _("Blue base");
285 case "danger": return _("DANGER");
286 case "enemyflagcarrier": return _("Enemy carrier");
287 case "flagcarrier": return _("Flag carrier");
288 case "flagdropped": return _("Dropped flag");
289 case "helpme": return _("Help me!");
290 case "here": return _("Here");
291 case "key-dropped": return _("Dropped key");
292 case "keycarrier-blue": return _("Key carrier");
293 case "keycarrier-finish": return _("Run here");
294 case "keycarrier-friend": return _("Key carrier");
295 case "keycarrier-pink": return _("Key carrier");
296 case "keycarrier-red": return _("Key carrier");
297 case "keycarrier-yellow": return _("Key carrier");
298 case "redbase": return _("Red base");
299 case "yellowbase": return _("Yellow base");
300 case "neutralbase": return _("White base");
301 case "pinkbase": return _("Pink base");
302 case "waypoint": return _("Waypoint");
303 case "ons-gen": return _("Generator");
304 case "ons-gen-shielded": return _("Generator");
305 case "ons-cp": return _("Control point");
306 case "ons-cp-atck": return _("Control point");
307 case "ons-cp-dfnd": return _("Control point");
308 case "race-checkpoint": return _("Checkpoint");
309 case "race-finish": return _("Finish");
310 case "race-start": return _("Start");
311 case "race-start-finish": return (race_checkpointtime || race_mycheckpointtime) ? _("Finish") : _("Start");
312 case "goal": return _("Goal");
313 case "nb-ball": return _("Ball");
314 case "ka-ball": return _("Ball");
315 case "ka-ballcarrier": return _("Ball carrier");
316 case "dom-neut": return _("Control point");
317 case "dom-red": return _("Control point");
318 case "dom-blue": return _("Control point");
319 case "dom-yellow": return _("Control point");
320 case "dom-pink": return _("Control point");
321 case "item-invis": return _("Invisibility");
322 case "item-extralife": return _("Extra life");
323 case "item-speed": return _("Speed");
324 case "frozen": return _("Frozen!");
325 case "tagged-target": return _("Tagged");
326 case "vehicle": return _("Vehicle");
327 case "intruder": return _("Intruder!");
334 void drawrotpic(vector org, float rot, string pic, vector sz, vector hotspot, vector rgb, float a, float f)
336 vector v1, v2, v3, v4;
338 hotspot = -1 * hotspot;
340 // hotspot-relative coordinates of the corners
342 v2 = hotspot + '1 0 0' * sz.x;
343 v3 = hotspot + '1 0 0' * sz.x + '0 1 0' * sz.y;
344 v4 = hotspot + '0 1 0' * sz.y;
346 // rotate them, and make them absolute
347 rot = -rot; // rotate by the opposite angle, as our coordinate system is reversed
348 v1 = rotate(v1, rot) + org;
349 v2 = rotate(v2, rot) + org;
350 v3 = rotate(v3, rot) + org;
351 v4 = rotate(v4, rot) + org;
354 R_BeginPolygon(pic, f);
355 R_PolygonVertex(v1, '0 0 0', rgb, a);
356 R_PolygonVertex(v2, '1 0 0', rgb, a);
357 R_PolygonVertex(v3, '1 1 0', rgb, a);
358 R_PolygonVertex(v4, '0 1 0', rgb, a);
362 void drawquad(vector o, vector ri, vector up, string pic, vector rgb, float a, float f)
364 R_BeginPolygon(pic, f);
365 R_PolygonVertex(o, '0 0 0', rgb, a);
366 R_PolygonVertex(o + ri, '1 0 0', rgb, a);
367 R_PolygonVertex(o + up + ri, '1 1 0', rgb, a);
368 R_PolygonVertex(o + up, '0 1 0', rgb, a);
372 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)
375 float owidth; // outer width
377 hotspot = -1 * hotspot;
379 // hotspot-relative coordinates of the healthbar corners
384 rot = -rot; // rotate by the opposite angle, as our coordinate system is reversed
385 o = rotate(o, rot) + org;
386 ri = rotate(ri, rot);
387 up = rotate(up, rot);
389 owidth = width + 2 * border;
390 o = o - up * (margin + border + theheight) + ri * (sz.x - owidth) * 0.5;
392 drawquad(o - up * border, ri * owidth, up * border, "", rgb, a, f);
393 drawquad(o + up * theheight, ri * owidth, up * border, "", rgb, a, f);
394 drawquad(o, ri * border, up * theheight, "", rgb, a, f);
395 drawquad(o + ri * (owidth - border), ri * border, up * theheight, "", rgb, a, f);
396 drawquad(o + ri * (border + align * ((1 - h) * width)), ri * width * h, up * theheight, "", hrgb, ha, f);
399 // returns location of sprite text
400 vector drawspritearrow(vector o, float ang, vector rgb, float a, float t)
402 float size = 9.0 * t;
403 float border = 1.5 * t;
404 float margin = 4.0 * t;
406 float borderDiag = border * 1.414;
407 vector arrowX = eX * size;
408 vector arrowY = eY * (size+borderDiag);
409 vector borderX = eX * (size+borderDiag);
410 vector borderY = eY * (size+borderDiag+border);
412 R_BeginPolygon("", DRAWFLAG_NORMAL);
413 R_PolygonVertex(o, '0 0 0', '0 0 0', a);
414 R_PolygonVertex(o + rotate(arrowY - borderX, ang), '0 0 0', '0 0 0', a);
415 R_PolygonVertex(o + rotate(borderY - borderX, ang), '0 0 0', '0 0 0', a);
416 R_PolygonVertex(o + rotate(borderY + borderX, ang), '0 0 0', '0 0 0', a);
417 R_PolygonVertex(o + rotate(arrowY + borderX, ang), '0 0 0', '0 0 0', a);
420 R_BeginPolygon("", DRAWFLAG_ADDITIVE);
421 R_PolygonVertex(o + rotate(eY * borderDiag, ang), '0 0 0', rgb, a);
422 R_PolygonVertex(o + rotate(arrowY - arrowX, ang), '0 0 0', rgb, a);
423 R_PolygonVertex(o + rotate(arrowY + arrowX, ang), '0 0 0', rgb, a);
426 return o + rotate(eY * (borderDiag+size+margin), ang);
429 // returns location of sprite healthbar
430 vector drawspritetext(vector o, float ang, float minwidth, vector rgb, float a, vector fontsize, string s)
434 float aspect, sa, ca;
436 sw = stringwidth(s, false, fontsize);
443 // how do corners work?
444 aspect = vid_conwidth / vid_conheight;
446 ca = cos(ang) * aspect;
447 if (fabs(sa) > fabs(ca))
450 algny = 0.5 - 0.5 * ca / fabs(sa);
454 algnx = 0.5 - 0.5 * sa / fabs(ca);
462 // we want to be onscreen
467 if (o.x > vid_conwidth - w)
468 o.x = vid_conwidth - w;
469 if (o.y > vid_conheight - h)
470 o.x = vid_conheight - h;
472 o.x += 0.5 * (w - sw);
474 drawstring(o, s, fontsize, rgb, a, DRAWFLAG_NORMAL);
482 vector fixrgbexcess_move(vector rgb, vector src, vector dst)
484 vector yvec = '0.299 0.587 0.114';
485 return rgb + dst * ((src * yvec) / (dst * yvec)) * ((rgb - '1 1 1') * src);
488 vector fixrgbexcess(vector rgb)
491 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 1 1');
493 rgb = fixrgbexcess_move(rgb, '0 1 0', '0 0 1');
494 if (rgb.z > 1) rgb.z = 1;
495 } else if (rgb.z > 1) {
496 rgb = fixrgbexcess_move(rgb, '0 0 1', '0 1 0');
497 if (rgb.y > 1) rgb.y = 1;
499 } else if (rgb.y > 1) {
500 rgb = fixrgbexcess_move(rgb, '0 1 0', '1 0 1');
502 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 0 1');
503 if (rgb.z > 1) rgb.z = 1;
504 } else if (rgb.z > 1) {
505 rgb = fixrgbexcess_move(rgb, '0 0 1', '1 0 0');
506 if (rgb.x > 1) rgb.x = 1;
508 } else if (rgb.z > 1) {
509 rgb = fixrgbexcess_move(rgb, '0 0 1', '1 1 0');
511 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 1 0');
512 if (rgb.y > 1) rgb.y = 1;
513 } else if (rgb.y > 1) {
514 rgb = fixrgbexcess_move(rgb, '0 1 0', '1 0 0');
515 if (rgb.x > 1) rgb.x = 1;
521 void Draw_WaypointSprite()
524 self.alpha = pow(bound(0, (self.fadetime - time) / self.lifetime, 1), waypointsprite_timealphaexponent);
528 if (self.hideflags & 2)
529 return; // radar only
531 if (autocvar_cl_hidewaypoints >= 2)
534 if (self.hideflags & 1)
535 if (autocvar_cl_hidewaypoints)
536 return; // fixed waypoint
538 InterpolateOrigin_Do();
540 float t = GetPlayerColor(player_localnum) + 1;
542 string spriteimage = "";
547 case SPRITERULE_SPECTATOR:
549 (autocvar_g_waypointsprite_itemstime == 1 && t == NUM_SPECTATOR + 1)
550 || (autocvar_g_waypointsprite_itemstime == 2 && (t == NUM_SPECTATOR + 1 || warmup_stage))
553 spriteimage = self.netname;
555 case SPRITERULE_DEFAULT:
559 spriteimage = self.netname;
564 spriteimage = self.netname;
566 case SPRITERULE_TEAMPLAY:
567 if (t == NUM_SPECTATOR + 1)
568 spriteimage = self.netname3;
569 else if (self.team == t)
570 spriteimage = self.netname2;
572 spriteimage = self.netname;
575 error("Invalid waypointsprite rule!");
579 if (spriteimage == "")
582 ++waypointsprite_newcount;
585 dist = vlen(self.origin - view_origin);
588 a = self.alpha * autocvar_hud_panel_fg_alpha;
590 if (self.maxdistance > waypointsprite_normdistance)
591 a *= pow(bound(0, (self.maxdistance - dist) / (self.maxdistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent);
592 else if (self.maxdistance > 0)
593 a *= pow(bound(0, (waypointsprite_fadedistance - dist) / (waypointsprite_fadedistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent) * (1 - waypointsprite_minalpha) + waypointsprite_minalpha;
595 vector rgb = spritelookupcolor(spriteimage, self.teamradar_color);
598 self.teamradar_color = '1 0 1';
599 printf("WARNING: sprite of name %s has no color, using pink so you notice it\n", spriteimage);
602 if (time - floor(time) > 0.5)
604 if (self.helpme && time < self.helpme)
605 a *= SPRITE_HELPME_BLINK;
606 else if (!self.lifetime) // fading out waypoints don't blink
607 a *= spritelookupblinkvalue(spriteimage);
619 rgb = fixrgbexcess(rgb);
624 o = project_3d_to_2d(self.origin);
626 || o.x < (vid_conwidth * waypointsprite_edgeoffset_left)
627 || o.y < (vid_conheight * waypointsprite_edgeoffset_top)
628 || o.x > (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right))
629 || o.y > (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom)))
631 // scale it to be just in view
635 d = o - '0.5 0 0' * vid_conwidth - '0 0.5 0' * vid_conheight;
636 ang = atan2(-d.x, -d.y);
640 f1 = d.x / vid_conwidth;
641 f2 = d.y / vid_conheight;
643 if (max(f1, -f1) > max(f2, -f2)) {
646 d = d * ((0.5 - waypointsprite_edgeoffset_right) / f1);
649 d = d * (-(0.5 - waypointsprite_edgeoffset_left) / f1);
654 d = d * ((0.5 - waypointsprite_edgeoffset_bottom) / f2);
657 d = d * (-(0.5 - waypointsprite_edgeoffset_top) / f2);
661 o = d + '0.5 0 0' * vid_conwidth + '0 0.5 0' * vid_conheight;
669 d = o - '0.5 0 0' * vid_conwidth - '0 0.5 0' * vid_conheight;
670 ang = atan2(-d.x, -d.y);
675 float edgedistance_min = min((o.y - (vid_conheight * waypointsprite_edgeoffset_top)),
676 (o.x - (vid_conwidth * waypointsprite_edgeoffset_left)),
677 (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right)) - o.x,
678 (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom)) - o.y);
680 float vidscale = max(vid_conwidth / vid_width, vid_conheight / vid_height);
682 float crosshairdistance = sqrt( pow(o.x - vid_conwidth/2, 2) + pow(o.y - vid_conheight/2, 2) );
684 t = waypointsprite_scale * vidscale;
685 a *= waypointsprite_alpha;
688 a = a * (1 - (1 - waypointsprite_distancefadealpha) * (bound(0, dist/waypointsprite_distancefadedistance, 1)));
689 t = t * (1 - (1 - waypointsprite_distancefadescale) * (bound(0, dist/waypointsprite_distancefadedistance, 1)));
691 if (edgedistance_min < waypointsprite_edgefadedistance) {
692 a = a * (1 - (1 - waypointsprite_edgefadealpha) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1)));
693 t = t * (1 - (1 - waypointsprite_edgefadescale) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1)));
695 if (crosshairdistance < waypointsprite_crosshairfadedistance) {
696 a = a * (1 - (1 - waypointsprite_crosshairfadealpha) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1)));
697 t = t * (1 - (1 - waypointsprite_crosshairfadescale) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1)));
700 if (self.build_finished)
702 if (time < self.build_finished + 0.25)
704 if (time < self.build_started)
705 self.health = self.build_starthealth;
706 else if (time < self.build_finished)
707 self.health = (time - self.build_started) / (self.build_finished - self.build_started) * (1 - self.build_starthealth) + self.build_starthealth;
715 o = drawspritearrow(o, ang, rgb, a, SPRITE_ARROW_SCALE * t);
718 if (autocvar_g_waypointsprite_spam && waypointsprite_count >= autocvar_g_waypointsprite_spam)
721 txt = spritelookuptext(spriteimage);
722 if (self.helpme && time < self.helpme)
723 txt = sprintf(_("%s needing help!"), txt);
724 if (autocvar_g_waypointsprite_uppercase)
725 txt = strtoupper(txt);
727 draw_beginBoldFont();
728 if (self.health >= 0)
730 o = drawspritetext(o, ang, (SPRITE_HEALTHBAR_WIDTH + 2 * SPRITE_HEALTHBAR_BORDER) * t, rgb, a, waypointsprite_fontsize * '1 1 0', txt);
733 if (self.build_finished)
738 marg = -(SPRITE_HEALTHBAR_MARGIN + SPRITE_HEALTHBAR_HEIGHT + 2 * SPRITE_HEALTHBAR_BORDER) * t - 0.5 * waypointsprite_fontsize;
740 marg = SPRITE_HEALTHBAR_MARGIN * t + 0.5 * waypointsprite_fontsize;
747 SPRITE_HEALTHBAR_WIDTH * t,
748 SPRITE_HEALTHBAR_HEIGHT * t,
750 SPRITE_HEALTHBAR_BORDER * t,
753 a * SPRITE_HEALTHBAR_BORDERALPHA,
755 a * SPRITE_HEALTHBAR_HEALTHALPHA,
761 o = drawspritetext(o, ang, 0, rgb, a, waypointsprite_fontsize * '1 1 0', txt);
766 void WaypointSprite_Load_Frames(string ext)
768 int dh = search_begin(strcat("models/sprites/*_frame*", ext), false, false);
770 int ext_len = strlen(ext);
771 int n = search_getsize(dh);
772 for (int i = 0; i < n; ++i)
774 string s = search_getfilename(dh, i);
775 s = substring(s, 15, strlen(s) - 15 - ext_len); // strip models/sprites/ and extension
777 int o = strstrofs(s, "_frame", 0);
778 string sname = strcat("/spriteframes/", substring(s, 0, o));
779 string sframes = substring(s, o + 6, strlen(s) - o - 6);
780 int f = stof(sframes) + 1;
781 db_put(tempdb, sname, ftos(max(f, stof(db_get(tempdb, sname)))));
786 void WaypointSprite_Load()
788 waypointsprite_fadedistance = vlen(mi_scale);
789 waypointsprite_normdistance = autocvar_g_waypointsprite_normdistance;
790 waypointsprite_minscale = autocvar_g_waypointsprite_minscale;
791 waypointsprite_minalpha = autocvar_g_waypointsprite_minalpha;
792 waypointsprite_distancealphaexponent = autocvar_g_waypointsprite_distancealphaexponent;
793 waypointsprite_timealphaexponent = autocvar_g_waypointsprite_timealphaexponent;
794 waypointsprite_scale = autocvar_g_waypointsprite_scale;
795 waypointsprite_fontsize = autocvar_g_waypointsprite_fontsize;
796 waypointsprite_edgefadealpha = autocvar_g_waypointsprite_edgefadealpha;
797 waypointsprite_edgefadescale = autocvar_g_waypointsprite_edgefadescale;
798 waypointsprite_edgefadedistance = autocvar_g_waypointsprite_edgefadedistance;
799 waypointsprite_edgeoffset_bottom = autocvar_g_waypointsprite_edgeoffset_bottom;
800 waypointsprite_edgeoffset_left = autocvar_g_waypointsprite_edgeoffset_left;
801 waypointsprite_edgeoffset_right = autocvar_g_waypointsprite_edgeoffset_right;
802 waypointsprite_edgeoffset_top = autocvar_g_waypointsprite_edgeoffset_top;
803 waypointsprite_crosshairfadealpha = autocvar_g_waypointsprite_crosshairfadealpha;
804 waypointsprite_crosshairfadescale = autocvar_g_waypointsprite_crosshairfadescale;
805 waypointsprite_crosshairfadedistance = autocvar_g_waypointsprite_crosshairfadedistance;
806 waypointsprite_distancefadealpha = autocvar_g_waypointsprite_distancefadealpha;
807 waypointsprite_distancefadescale = autocvar_g_waypointsprite_distancefadescale;
808 waypointsprite_distancefadedistance = waypointsprite_fadedistance * autocvar_g_waypointsprite_distancefadedistancemultiplier;
809 waypointsprite_alpha = autocvar_g_waypointsprite_alpha * (1 - autocvar__menu_alpha);
811 if (!waypointsprite_initialized)
813 WaypointSprite_Load_Frames(".tga");
814 WaypointSprite_Load_Frames(".jpg");
815 waypointsprite_initialized = true;
818 waypointsprite_count = waypointsprite_newcount;
819 waypointsprite_newcount = 0;
824 void WaypointSprite_UpdateSprites(entity e, string m1, string m2, string m3)
843 void WaypointSprite_UpdateHealth(entity e, float f)
845 f = bound(0, f, e.max_health);
846 if (f != e.health || e.pain_finished)
854 void WaypointSprite_UpdateMaxHealth(entity e, float f)
856 if (f != e.max_health || e.pain_finished)
864 void WaypointSprite_UpdateBuildFinished(entity e, float f)
866 if (f != e.pain_finished || e.max_health)
874 void WaypointSprite_UpdateOrigin(entity e, vector o)
883 void WaypointSprite_UpdateRule(entity e, float t, float r)
885 // no check, as this is never called without doing an actual change (usually only once)
891 void WaypointSprite_UpdateTeamRadar(entity e, float icon, vector col)
893 // no check, as this is never called without doing an actual change (usually only once)
894 e.cnt = (icon & 0x7F) | (e.cnt & 0x80);
899 void WaypointSprite_Ping(entity e)
902 if (time < e.waypointsprite_pingtime) return;
903 e.waypointsprite_pingtime = time + 0.3;
904 // ALWAYS sends (this causes a radar circle), thus no check
909 void WaypointSprite_HelpMePing(entity e)
911 WaypointSprite_Ping(e);
912 e.waypointsprite_helpmetime = time + waypointsprite_deployed_lifetime;
916 void WaypointSprite_FadeOutIn(entity e, float t)
921 e.teleport_time = time + t;
923 else if (t < (e.teleport_time - time))
925 // accelerate the waypoint's dying
927 // (e.teleport_time - time) / wp.fade_time stays
928 // e.teleport_time = time + fadetime
929 float current_fadetime;
930 current_fadetime = e.teleport_time - time;
931 e.teleport_time = time + t;
932 e.fade_time = e.fade_time * t / current_fadetime;
938 void WaypointSprite_Init()
940 waypointsprite_limitedrange = autocvar_sv_waypointsprite_limitedrange;
941 waypointsprite_deployed_lifetime = autocvar_sv_waypointsprite_deployed_lifetime;
942 waypointsprite_deadlifetime = autocvar_sv_waypointsprite_deadlifetime;
945 void WaypointSprite_InitClient(entity e)
949 void WaypointSprite_Kill(entity wp)
952 if (wp.owner) wp.owner.(wp.owned_by_field) = world;
956 void WaypointSprite_Disown(entity wp, float fadetime)
959 if (wp.classname != "sprite_waypoint")
961 backtrace("Trying to disown a non-waypointsprite");
966 if (wp.exteriormodeltoclient == wp.owner)
967 wp.exteriormodeltoclient = world;
968 wp.owner.(wp.owned_by_field) = world;
971 WaypointSprite_FadeOutIn(wp, fadetime);
975 void WaypointSprite_Think()
977 bool doremove = false;
979 if (self.fade_time && time >= self.teleport_time)
984 if (self.exteriormodeltoclient)
985 WaypointSprite_UpdateOrigin(self, self.exteriormodeltoclient.origin + self.view_ofs);
988 WaypointSprite_Kill(self);
990 self.nextthink = time; // WHY?!?
993 float WaypointSprite_visible_for_player(entity e)
995 // personal waypoints
996 if (self.enemy && self.enemy != e)
1000 if (self.rule == SPRITERULE_SPECTATOR)
1002 if (!autocvar_sv_itemstime)
1004 if (!warmup_stage && IS_PLAYER(e))
1007 else if (self.team && self.rule == SPRITERULE_DEFAULT)
1009 if (self.team != e.team)
1018 entity WaypointSprite_getviewentity(entity e)
1020 if (IS_SPEC(e)) e = e.enemy;
1021 /* TODO idea (check this breaks nothing)
1022 else if (e.classname == "observer")
1028 float WaypointSprite_isteammate(entity e, entity e2)
1031 return e2.team == e.team;
1035 float WaypointSprite_Customize()
1037 // this is not in SendEntity because it shall run every frame, not just every update
1039 // make spectators see what the player would see
1040 entity e = WaypointSprite_getviewentity(other);
1042 if (MUTATOR_CALLHOOK(CustomizeWaypoint, self, other))
1045 return self.waypointsprite_visible_for_player(e);
1048 float WaypointSprite_SendEntity(entity to, float sendflags);
1050 void WaypointSprite_Reset()
1052 // if a WP wants to time out, let it time out immediately; other WPs ought to be reset/killed by their owners
1054 if (self.fade_time) // there was there before: || g_keyhunt, do we really need this?
1055 WaypointSprite_Kill(self);
1058 entity WaypointSprite_Spawn(
1059 string spr, // sprite
1060 float _lifetime, float maxdistance, // lifetime, max distance
1061 entity ref, vector ofs, // position
1062 entity showto, float t, // show to whom? Use a flag to indicate a team
1063 entity own, .entity ownfield, // remove when own gets killed
1064 float hideable, // true when it should be controlled by cl_hidewaypoints
1065 float icon, vector rgb // initial icon and color
1068 entity wp = new(sprite_waypoint);
1069 wp.teleport_time = time + _lifetime;
1070 wp.fade_time = _lifetime;
1071 wp.exteriormodeltoclient = ref;
1075 setorigin(wp, ref.origin + ofs);
1082 wp.currentammo = hideable;
1086 remove(own.(ownfield));
1087 own.(ownfield) = wp;
1088 wp.owned_by_field = ownfield;
1090 wp.fade_rate = maxdistance;
1091 wp.think = WaypointSprite_Think;
1092 wp.nextthink = time;
1094 wp.customizeentityforclient = WaypointSprite_Customize;
1095 wp.waypointsprite_visible_for_player = WaypointSprite_visible_for_player;
1096 wp.reset2 = WaypointSprite_Reset;
1099 Net_LinkEntity(wp, false, 0, WaypointSprite_SendEntity);
1103 entity WaypointSprite_SpawnFixed(
1108 float icon, vector rgb // initial icon and color
1111 return WaypointSprite_Spawn(spr, 0, 0, world, ofs, world, 0, own, ownfield, true, icon, rgb);
1114 entity WaypointSprite_DeployFixed(
1116 float limited_range,
1118 float icon, vector rgb // initial icon and color
1128 maxdistance = waypointsprite_limitedrange;
1131 return WaypointSprite_Spawn(spr, waypointsprite_deployed_lifetime, maxdistance, world, ofs, world, t, self, waypointsprite_deployed_fixed, false, icon, rgb);
1134 entity WaypointSprite_DeployPersonal(
1137 float icon, vector rgb // initial icon and color
1140 return WaypointSprite_Spawn(spr, 0, 0, world, ofs, world, 0, self, waypointsprite_deployed_personal, false, icon, rgb);
1143 entity WaypointSprite_Attach(
1145 float limited_range,
1146 float icon, vector rgb // initial icon and color
1150 if (self.waypointsprite_attachedforcarrier)
1151 return world; // can't attach to FC
1158 maxdistance = waypointsprite_limitedrange;
1161 return WaypointSprite_Spawn(spr, waypointsprite_deployed_lifetime, maxdistance, self, '0 0 64', world, t, self, waypointsprite_attached, false, icon, rgb);
1164 entity WaypointSprite_AttachCarrier(
1167 float icon, vector rgb // initial icon and color
1170 WaypointSprite_Kill(carrier.waypointsprite_attached); // FC overrides attached
1171 entity e = WaypointSprite_Spawn(spr, 0, 0, carrier, '0 0 64', world, carrier.team, carrier, waypointsprite_attachedforcarrier, false, icon, rgb);
1174 WaypointSprite_UpdateMaxHealth(e, '1 0 0' * healtharmor_maxdamage(start_health, start_armorvalue, autocvar_g_balance_armor_blockpercent, DEATH_WEAPON) * 2);
1175 WaypointSprite_UpdateHealth(e, '1 0 0' * healtharmor_maxdamage(carrier.health, carrier.armorvalue, autocvar_g_balance_armor_blockpercent, DEATH_WEAPON));
1180 void WaypointSprite_DetachCarrier(entity carrier)
1182 WaypointSprite_Disown(carrier.waypointsprite_attachedforcarrier, waypointsprite_deadlifetime);
1185 void WaypointSprite_ClearPersonal()
1187 WaypointSprite_Kill(self.waypointsprite_deployed_personal);
1190 void WaypointSprite_ClearOwned()
1192 WaypointSprite_Kill(self.waypointsprite_deployed_fixed);
1193 WaypointSprite_Kill(self.waypointsprite_deployed_personal);
1194 WaypointSprite_Kill(self.waypointsprite_attached);
1197 void WaypointSprite_PlayerDead()
1199 WaypointSprite_Disown(self.waypointsprite_attached, waypointsprite_deadlifetime);
1200 WaypointSprite_DetachCarrier(self);
1203 void WaypointSprite_PlayerGone()
1205 WaypointSprite_Disown(self.waypointsprite_deployed_fixed, waypointsprite_deadlifetime);
1206 WaypointSprite_Kill(self.waypointsprite_deployed_personal);
1207 WaypointSprite_Disown(self.waypointsprite_attached, waypointsprite_deadlifetime);
1208 WaypointSprite_DetachCarrier(self);