1 #include "waypointsprites.qh"
3 REGISTER_MUTATOR(waypointsprites, true);
5 REGISTER_NET_LINKED(waypointsprites)
8 /** flags origin [team displayrule] [spritename] [spritename2] [spritename3] [lifetime maxdistance hideable] */
9 bool WaypointSprite_SendEntity(entity this, entity to, float sendflags)
11 WriteHeader(MSG_ENTITY, waypointsprites);
13 sendflags = sendflags & 0x7F;
15 if (this.max_health || (this.pain_finished && (time < this.pain_finished + 0.25)))
19 if(this.currentammo == 1)
21 if(this.exteriormodeltoclient == to)
23 if(this.currentammo == 2)
26 MUTATOR_CALLHOOK(SendWaypoint, this, to, sendflags, f);
27 sendflags = M_ARGV(2, int);
30 WriteByte(MSG_ENTITY, sendflags);
31 WriteByte(MSG_ENTITY, this.wp_extra);
37 WriteByte(MSG_ENTITY, (this.health / this.max_health) * 191.0);
41 float dt = this.pain_finished - time;
42 dt = bound(0, dt * 32, 16383);
43 WriteByte(MSG_ENTITY, (dt & 0xFF00) / 256 + 192);
44 WriteByte(MSG_ENTITY, (dt & 0x00FF));
50 WriteCoord(MSG_ENTITY, this.origin.x);
51 WriteCoord(MSG_ENTITY, this.origin.y);
52 WriteCoord(MSG_ENTITY, this.origin.z);
57 WriteByte(MSG_ENTITY, this.team);
58 WriteByte(MSG_ENTITY, this.rule);
62 WriteString(MSG_ENTITY, this.model1);
65 WriteString(MSG_ENTITY, this.model2);
68 WriteString(MSG_ENTITY, this.model3);
72 WriteCoord(MSG_ENTITY, this.fade_time);
73 WriteCoord(MSG_ENTITY, this.teleport_time);
74 WriteShort(MSG_ENTITY, this.fade_rate); // maxdist
75 WriteByte(MSG_ENTITY, f);
80 WriteByte(MSG_ENTITY, this.cnt); // icon on radar
81 WriteByte(MSG_ENTITY, this.colormod.x * 255.0);
82 WriteByte(MSG_ENTITY, this.colormod.y * 255.0);
83 WriteByte(MSG_ENTITY, this.colormod.z * 255.0);
85 if (WaypointSprite_isteammate(this.owner, WaypointSprite_getviewentity(to)))
87 float dt = (this.waypointsprite_helpmetime - time) / 0.1;
92 WriteByte(MSG_ENTITY, dt);
95 WriteByte(MSG_ENTITY, 0);
103 void Ent_WaypointSprite(entity this, bool isnew);
104 NET_HANDLE(waypointsprites, bool isnew) {
105 Ent_WaypointSprite(this, isnew);
109 void Ent_RemoveWaypointSprite(entity this)
111 if (this.netname) strunzone(this.netname);
112 if (this.netname2) strunzone(this.netname2);
113 if (this.netname3) strunzone(this.netname3);
116 /** flags origin [team displayrule] [spritename] [spritename2] [spritename3] [lifetime maxdistance hideable] */
117 void Ent_WaypointSprite(entity this, bool isnew)
119 int sendflags = ReadByte();
120 this.wp_extra = ReadByte();
123 this.spawntime = time;
125 this.draw2d = Draw_WaypointSprite;
127 IL_PUSH(g_drawables_2d, this);
128 IL_PUSH(g_radaricons, this);
131 InterpolateOrigin_Undo(this);
132 this.iflags |= IFLAG_ORIGIN;
134 if (sendflags & 0x80)
139 this.health = t / 191.0;
140 this.build_finished = 0;
144 t = (t - 192) * 256 + ReadByte();
145 this.build_started = servertime;
146 if (this.build_finished)
147 this.build_starthealth = bound(0, this.health, 1);
149 this.build_starthealth = 0;
150 this.build_finished = servertime + t / 32;
156 this.build_finished = 0;
161 // unfortunately, this needs to be exact (for the 3D display)
162 this.origin_x = ReadCoord();
163 this.origin_y = ReadCoord();
164 this.origin_z = ReadCoord();
165 setorigin(this, this.origin);
170 this.team = ReadByte();
171 this.rule = ReadByte();
177 strunzone(this.netname);
178 this.netname = strzone(ReadString());
184 strunzone(this.netname2);
185 this.netname2 = strzone(ReadString());
191 strunzone(this.netname3);
192 this.netname3 = strzone(ReadString());
197 this.lifetime = ReadCoord();
198 this.fadetime = ReadCoord();
199 this.maxdistance = ReadShort();
200 this.hideflags = ReadByte();
206 this.teamradar_icon = f & BITS(7);
209 this.(teamradar_times[this.teamradar_time_index]) = time;
210 this.teamradar_time_index = (this.teamradar_time_index + 1) % MAX_TEAMRADAR_TIMES;
212 this.teamradar_color_x = ReadByte() / 255.0;
213 this.teamradar_color_y = ReadByte() / 255.0;
214 this.teamradar_color_z = ReadByte() / 255.0;
215 this.helpme = ReadByte() * 0.1;
217 this.helpme += servertime;
220 InterpolateOrigin_Note(this);
222 this.entremove = Ent_RemoveWaypointSprite;
227 float spritelookupblinkvalue(entity this, string s)
229 if (s == WP_Weapon.netname) {
230 if (Weapons_from(this.wp_extra).spawnflags & WEP_FLAG_SUPERWEAPON)
233 if (s == WP_Item.netname) return Items_from(this.wp_extra).m_waypointblink;
234 if(s == WP_FlagReturn.netname) return 2;
239 vector spritelookupcolor(entity this, string s, vector def)
241 if (s == WP_Weapon.netname || s == RADARICON_Weapon.netname) return Weapons_from(this.wp_extra).wpcolor;
242 if (s == WP_Item.netname || s == RADARICON_Item.netname) return Items_from(this.wp_extra).m_color;
243 if (MUTATOR_CALLHOOK(WP_Format, this, s))
245 return M_ARGV(2, vector);
250 string spritelookuptext(entity this, string s)
252 if (s == WP_RaceStartFinish.netname) return (race_checkpointtime || race_mycheckpointtime) ? _("Finish") : _("Start");
253 if (s == WP_Weapon.netname) return Weapons_from(this.wp_extra).m_name;
254 if (s == WP_Item.netname) return Items_from(this.wp_extra).m_waypoint;
255 if (s == WP_Monster.netname) return get_monsterinfo(this.wp_extra).monster_name;
256 if (MUTATOR_CALLHOOK(WP_Format, this, s))
258 return M_ARGV(3, string);
261 // need to loop, as our netname could be one of three
262 FOREACH(Waypoints, it.netname == s, {
269 string spritelookupicon(entity this, string s)
271 // TODO: needs icons! //if (s == WP_RaceStartFinish.netname) return (race_checkpointtime || race_mycheckpointtime) ? _("Finish") : _("Start");
272 if (s == WP_Weapon.netname) return Weapons_from(this.wp_extra).model2;
273 if (s == WP_Item.netname) return Items_from(this.wp_extra).m_icon;
274 if (s == WP_Buff.netname) return strcat("buff_", Buffs_from(this.wp_extra).m_name);
275 if (s == WP_Vehicle.netname) return Vehicles_from(this.wp_extra).m_icon;
276 //if (s == WP_Monster.netname) return get_monsterinfo(this.wp_extra).m_icon;
277 if (MUTATOR_CALLHOOK(WP_Format, this, s))
279 return M_ARGV(4, string);
282 // need to loop, as our netname could be one of three
283 FOREACH(Waypoints, it.netname == s, {
292 void drawrotpic(vector org, float rot, string pic, vector sz, vector hotspot, vector rgb, float a, float f)
294 vector v1, v2, v3, v4;
296 hotspot = -1 * hotspot;
298 // hotspot-relative coordinates of the corners
300 v2 = hotspot + '1 0 0' * sz.x;
301 v3 = hotspot + '1 0 0' * sz.x + '0 1 0' * sz.y;
302 v4 = hotspot + '0 1 0' * sz.y;
304 // rotate them, and make them absolute
305 rot = -rot; // rotate by the opposite angle, as our coordinate system is reversed
306 v1 = Rotate(v1, rot) + org;
307 v2 = Rotate(v2, rot) + org;
308 v3 = Rotate(v3, rot) + org;
309 v4 = Rotate(v4, rot) + org;
312 R_BeginPolygon(pic, f);
313 R_PolygonVertex(v1, '0 0 0', rgb, a);
314 R_PolygonVertex(v2, '1 0 0', rgb, a);
315 R_PolygonVertex(v3, '1 1 0', rgb, a);
316 R_PolygonVertex(v4, '0 1 0', rgb, a);
320 void drawquad(vector o, vector ri, vector up, string pic, vector rgb, float a, float f)
322 R_BeginPolygon(pic, f);
323 R_PolygonVertex(o, '0 0 0', rgb, a);
324 R_PolygonVertex(o + ri, '1 0 0', rgb, a);
325 R_PolygonVertex(o + up + ri, '1 1 0', rgb, a);
326 R_PolygonVertex(o + up, '0 1 0', rgb, a);
330 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)
333 float owidth; // outer width
335 hotspot = -1 * hotspot;
337 // hotspot-relative coordinates of the healthbar corners
342 rot = -rot; // rotate by the opposite angle, as our coordinate system is reversed
343 o = Rotate(o, rot) + org;
344 ri = Rotate(ri, rot);
345 up = Rotate(up, rot);
347 owidth = width + 2 * border;
348 o = o - up * (margin + border + theheight) + ri * (sz.x - owidth) * 0.5;
350 drawquad(o - up * border, ri * owidth, up * border, "", rgb, a, f);
351 drawquad(o + up * theheight, ri * owidth, up * border, "", rgb, a, f);
352 drawquad(o, ri * border, up * theheight, "", rgb, a, f);
353 drawquad(o + ri * (owidth - border), ri * border, up * theheight, "", rgb, a, f);
354 drawquad(o + ri * (border + align * ((1 - h) * width)), ri * width * h, up * theheight, "", hrgb, ha, f);
357 // returns location of sprite text
358 vector drawspritearrow(vector o, float ang, vector rgb, float a, float t)
360 float size = 9.0 * t;
361 float border = 1.5 * t;
362 float margin = 4.0 * t;
364 float borderDiag = border * 1.414;
365 vector arrowX = eX * size;
366 vector arrowY = eY * (size+borderDiag);
367 vector borderX = eX * (size+borderDiag);
368 vector borderY = eY * (size+borderDiag+border);
370 R_BeginPolygon("", DRAWFLAG_NORMAL);
371 R_PolygonVertex(o, '0 0 0', '0 0 0', a);
372 R_PolygonVertex(o + Rotate(arrowY - borderX, ang), '0 0 0', '0 0 0', a);
373 R_PolygonVertex(o + Rotate(borderY - borderX, ang), '0 0 0', '0 0 0', a);
374 R_PolygonVertex(o + Rotate(borderY + borderX, ang), '0 0 0', '0 0 0', a);
375 R_PolygonVertex(o + Rotate(arrowY + borderX, ang), '0 0 0', '0 0 0', a);
378 R_BeginPolygon("", DRAWFLAG_ADDITIVE);
379 R_PolygonVertex(o + Rotate(eY * borderDiag, ang), '0 0 0', rgb, a);
380 R_PolygonVertex(o + Rotate(arrowY - arrowX, ang), '0 0 0', rgb, a);
381 R_PolygonVertex(o + Rotate(arrowY + arrowX, ang), '0 0 0', rgb, a);
384 return o + Rotate(eY * (borderDiag+size+margin), ang);
387 // returns location of sprite healthbar
388 vector drawspritetext(vector o, float ang, float minwidth, vector rgb, float a, vector fontsize, string s)
392 float aspect, sa, ca;
394 sw = stringwidth(s, false, fontsize);
401 // how do corners work?
402 aspect = vid_conwidth / vid_conheight;
404 ca = cos(ang) * aspect;
405 if (fabs(sa) > fabs(ca))
409 algny = 0.5 - 0.5 * (f ? (ca / f) : 0);
414 algnx = 0.5 - 0.5 * (f ? (sa / f) : 0);
422 // we want to be onscreen
427 if (o.x > vid_conwidth - w)
428 o.x = vid_conwidth - w;
429 if (o.y > vid_conheight - h)
430 o.x = vid_conheight - h;
432 o.x += 0.5 * (w - sw);
434 drawstring(o, s, fontsize, rgb, a, DRAWFLAG_NORMAL);
442 vector fixrgbexcess_move(vector rgb, vector src, vector dst)
444 vector yvec = '0.299 0.587 0.114';
445 return rgb + dst * ((src * yvec) / (dst * yvec)) * ((rgb - '1 1 1') * src);
448 vector fixrgbexcess(vector rgb)
451 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 1 1');
453 rgb = fixrgbexcess_move(rgb, '0 1 0', '0 0 1');
454 if (rgb.z > 1) rgb.z = 1;
455 } else if (rgb.z > 1) {
456 rgb = fixrgbexcess_move(rgb, '0 0 1', '0 1 0');
457 if (rgb.y > 1) rgb.y = 1;
459 } else if (rgb.y > 1) {
460 rgb = fixrgbexcess_move(rgb, '0 1 0', '1 0 1');
462 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 0 1');
463 if (rgb.z > 1) rgb.z = 1;
464 } else if (rgb.z > 1) {
465 rgb = fixrgbexcess_move(rgb, '0 0 1', '1 0 0');
466 if (rgb.x > 1) rgb.x = 1;
468 } else if (rgb.z > 1) {
469 rgb = fixrgbexcess_move(rgb, '0 0 1', '1 1 0');
471 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 1 0');
472 if (rgb.y > 1) rgb.y = 1;
473 } else if (rgb.y > 1) {
474 rgb = fixrgbexcess_move(rgb, '0 1 0', '1 0 0');
475 if (rgb.x > 1) rgb.x = 1;
481 void Draw_WaypointSprite(entity this)
483 if (this.lifetime > 0)
484 this.alpha = (bound(0, (this.fadetime - time) / this.lifetime, 1) ** waypointsprite_timealphaexponent);
488 if (this.hideflags & 2)
489 return; // radar only
491 if (autocvar_cl_hidewaypoints >= 2)
494 if (this.hideflags & 1 && autocvar_cl_hidewaypoints)
495 return; // fixed waypoint
497 InterpolateOrigin_Do(this);
499 float t = entcs_GetTeam(player_localnum) + 1;
500 string spriteimage = "";
505 case SPRITERULE_SPECTATOR:
507 (autocvar_g_waypointsprite_itemstime == 1 && t == NUM_SPECTATOR + 1)
508 || (autocvar_g_waypointsprite_itemstime == 2 && (t == NUM_SPECTATOR + 1 || warmup_stage || STAT(ITEMSTIME) == 2))
511 spriteimage = this.netname;
513 case SPRITERULE_DEFAULT:
517 spriteimage = this.netname;
522 spriteimage = this.netname;
524 case SPRITERULE_TEAMPLAY:
525 if (t == NUM_SPECTATOR + 1)
526 spriteimage = this.netname3;
527 else if (this.team == t)
528 spriteimage = this.netname2;
530 spriteimage = this.netname;
533 error("Invalid waypointsprite rule!");
537 if (spriteimage == "")
540 ++waypointsprite_newcount;
542 float dist = vlen(this.origin - view_origin);
543 float a = this.alpha * autocvar_hud_panel_fg_alpha;
545 if (this.maxdistance > waypointsprite_normdistance)
546 a *= (bound(0, (this.maxdistance - dist) / (this.maxdistance - waypointsprite_normdistance), 1) ** waypointsprite_distancealphaexponent);
547 else if (this.maxdistance > 0)
548 a *= (bound(0, (waypointsprite_fadedistance - dist) / (waypointsprite_fadedistance - waypointsprite_normdistance), 1) ** waypointsprite_distancealphaexponent) * (1 - waypointsprite_minalpha) + waypointsprite_minalpha;
550 vector rgb = spritelookupcolor(this, spriteimage, this.teamradar_color);
553 this.teamradar_color = '1 0 1';
554 LOG_INFOF("WARNING: sprite of name %s has no color, using pink so you notice it", spriteimage);
557 if (time - floor(time) > 0.5)
559 if (this.helpme && time < this.helpme)
560 a *= SPRITE_HELPME_BLINK;
561 else if (this.lifetime > 0) // fading out waypoints don't blink
562 a *= spritelookupblinkvalue(this, spriteimage);
574 rgb = fixrgbexcess(rgb);
579 o = project_3d_to_2d(this.origin);
581 || o.x < (vid_conwidth * waypointsprite_edgeoffset_left)
582 || o.y < (vid_conheight * waypointsprite_edgeoffset_top)
583 || o.x > (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right))
584 || o.y > (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom)))
586 // scale it to be just in view
590 d = o - '0.5 0 0' * vid_conwidth - '0 0.5 0' * vid_conheight;
591 ang = atan2(-d.x, -d.y);
595 f1 = d.x / vid_conwidth;
596 f2 = d.y / vid_conheight;
598 if (max(f1, -f1) > max(f2, -f2)) {
601 d = d * ((0.5 - waypointsprite_edgeoffset_right) / f1);
604 d = d * (-(0.5 - waypointsprite_edgeoffset_left) / f1);
609 d = d * ((0.5 - waypointsprite_edgeoffset_bottom) / f2);
612 d = d * (-(0.5 - waypointsprite_edgeoffset_top) / f2);
616 o = d + '0.5 0 0' * vid_conwidth + '0 0.5 0' * vid_conheight;
624 d = o - '0.5 0 0' * vid_conwidth - '0 0.5 0' * vid_conheight;
625 ang = atan2(-d.x, -d.y);
630 float edgedistance_min = min((o.y - (vid_conheight * waypointsprite_edgeoffset_top)),
631 (o.x - (vid_conwidth * waypointsprite_edgeoffset_left)),
632 (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right)) - o.x,
633 (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom)) - o.y);
635 float crosshairdistance = sqrt( ((o.x - vid_conwidth/2) ** 2) + ((o.y - vid_conheight/2) ** 2) );
637 t = waypointsprite_scale;
638 a *= waypointsprite_alpha;
641 a = a * (1 - (1 - waypointsprite_distancefadealpha) * (bound(0, dist/waypointsprite_distancefadedistance, 1)));
642 t = t * (1 - (1 - waypointsprite_distancefadescale) * (bound(0, dist/waypointsprite_distancefadedistance, 1)));
644 if (edgedistance_min < waypointsprite_edgefadedistance) {
645 a = a * (1 - (1 - waypointsprite_edgefadealpha) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1)));
646 t = t * (1 - (1 - waypointsprite_edgefadescale) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1)));
648 if (crosshairdistance < waypointsprite_crosshairfadedistance) {
649 a = a * (1 - (1 - waypointsprite_crosshairfadealpha) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1)));
650 t = t * (1 - (1 - waypointsprite_crosshairfadescale) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1)));
653 if (this.build_finished)
655 if (time < this.build_finished + 0.25)
657 if (time < this.build_started)
658 this.health = this.build_starthealth;
659 else if (time < this.build_finished)
660 this.health = (time - this.build_started) / (this.build_finished - this.build_started) * (1 - this.build_starthealth) + this.build_starthealth;
668 o = drawspritearrow(o, ang, rgb, a, SPRITE_ARROW_SCALE * t);
670 vector iconcolor = ((autocvar_g_waypointsprite_iconcolor) ? '1 1 1' : rgb);
671 string spr_icon = spritelookupicon(this, spriteimage);
672 string pic = spr_icon;
673 bool icon_found = !(!spr_icon || spr_icon == "");
674 if (icon_found) // it's valid, but let's make sure it exists!
676 pic = strcat(hud_skin_path, "/", spr_icon);
677 if(precache_pic(pic) == "")
679 pic = strcat("gfx/hud/default/", spr_icon);
680 if(!precache_pic(pic))
685 string txt = string_null;
686 if (autocvar_g_waypointsprite_text || !icon_found)
688 if (autocvar_g_waypointsprite_spam && waypointsprite_count >= autocvar_g_waypointsprite_spam)
691 txt = spritelookuptext(this, spriteimage);
692 if (this.helpme && time < this.helpme)
693 txt = sprintf(_("%s needing help!"), txt);
694 if (autocvar_g_waypointsprite_uppercase)
695 txt = strtoupper(txt);
698 draw_beginBoldFont();
699 if (this.health >= 0)
702 if (this.build_finished)
707 marg = -(SPRITE_HEALTHBAR_MARGIN + SPRITE_HEALTHBAR_HEIGHT + 2 * SPRITE_HEALTHBAR_BORDER) * t - 0.5 * waypointsprite_fontsize;
709 marg = SPRITE_HEALTHBAR_MARGIN * t + 0.5 * waypointsprite_fontsize;
716 SPRITE_HEALTHBAR_WIDTH * t,
717 SPRITE_HEALTHBAR_HEIGHT * t,
719 SPRITE_HEALTHBAR_BORDER * t,
722 a * SPRITE_HEALTHBAR_BORDERALPHA,
724 a * SPRITE_HEALTHBAR_HEALTHALPHA,
728 if(autocvar_g_waypointsprite_text || !icon_found)
729 o = drawspritetext(o, ang, (SPRITE_HEALTHBAR_WIDTH + 2 * SPRITE_HEALTHBAR_BORDER) * t, rgb, a, waypointsprite_fontsize * '1 1 0', txt);
731 drawpic(o - vec2(autocvar_g_waypointsprite_iconsize/2, autocvar_g_waypointsprite_iconsize*t + 2*marg + SPRITE_HEALTHBAR_HEIGHT*t), pic, '1 1 0'*autocvar_g_waypointsprite_iconsize, iconcolor, a, DRAWFLAG_NORMAL);
735 if (autocvar_g_waypointsprite_text || !icon_found)
736 o = drawspritetext(o, ang, 0, rgb, a, waypointsprite_fontsize * '1 1 0', txt);
738 drawpic(o - vec2(autocvar_g_waypointsprite_iconsize/2, autocvar_g_waypointsprite_iconsize*t + 2 + SPRITE_HEALTHBAR_HEIGHT*t), pic, '1 1 0'*autocvar_g_waypointsprite_iconsize, iconcolor, a, DRAWFLAG_NORMAL);
743 void WaypointSprite_Load_Frames(string ext)
745 int dh = search_begin(strcat("models/sprites/*_frame*", ext), false, false);
747 int ext_len = strlen(ext);
748 int n = search_getsize(dh);
749 for (int i = 0; i < n; ++i)
751 string s = search_getfilename(dh, i);
752 s = substring(s, 15, strlen(s) - 15 - ext_len); // strip models/sprites/ and extension
754 int o = strstrofs(s, "_frame", 0);
755 string sname = strcat("/spriteframes/", substring(s, 0, o));
756 string sframes = substring(s, o + 6, strlen(s) - o - 6);
757 int f = stof(sframes) + 1;
758 db_put(tempdb, sname, ftos(max(f, stof(db_get(tempdb, sname)))));
763 void WaypointSprite_Load();
764 STATIC_INIT(WaypointSprite_Load) {
765 WaypointSprite_Load();
766 WaypointSprite_Load_Frames(".tga");
767 WaypointSprite_Load_Frames(".jpg");
769 void WaypointSprite_Load()
771 waypointsprite_fadedistance = vlen(mi_scale);
772 waypointsprite_normdistance = autocvar_g_waypointsprite_normdistance;
773 waypointsprite_minscale = autocvar_g_waypointsprite_minscale;
774 waypointsprite_minalpha = autocvar_g_waypointsprite_minalpha;
775 waypointsprite_distancealphaexponent = autocvar_g_waypointsprite_distancealphaexponent;
776 waypointsprite_timealphaexponent = autocvar_g_waypointsprite_timealphaexponent;
777 waypointsprite_scale = autocvar_g_waypointsprite_scale;
778 waypointsprite_fontsize = autocvar_g_waypointsprite_fontsize;
779 waypointsprite_edgefadealpha = autocvar_g_waypointsprite_edgefadealpha;
780 waypointsprite_edgefadescale = autocvar_g_waypointsprite_edgefadescale;
781 waypointsprite_edgefadedistance = autocvar_g_waypointsprite_edgefadedistance;
782 waypointsprite_edgeoffset_bottom = autocvar_g_waypointsprite_edgeoffset_bottom;
783 waypointsprite_edgeoffset_left = autocvar_g_waypointsprite_edgeoffset_left;
784 waypointsprite_edgeoffset_right = autocvar_g_waypointsprite_edgeoffset_right;
785 waypointsprite_edgeoffset_top = autocvar_g_waypointsprite_edgeoffset_top;
786 waypointsprite_crosshairfadealpha = autocvar_g_waypointsprite_crosshairfadealpha;
787 waypointsprite_crosshairfadescale = autocvar_g_waypointsprite_crosshairfadescale;
788 waypointsprite_crosshairfadedistance = autocvar_g_waypointsprite_crosshairfadedistance;
789 waypointsprite_distancefadealpha = autocvar_g_waypointsprite_distancefadealpha;
790 waypointsprite_distancefadescale = autocvar_g_waypointsprite_distancefadescale;
791 waypointsprite_distancefadedistance = waypointsprite_fadedistance * autocvar_g_waypointsprite_distancefadedistancemultiplier;
792 waypointsprite_alpha = autocvar_g_waypointsprite_alpha * (1 - autocvar__menu_alpha);
794 waypointsprite_count = waypointsprite_newcount;
795 waypointsprite_newcount = 0;
800 void WaypointSprite_UpdateSprites(entity e, entity _m1, entity _m2, entity _m3)
802 string m1 = _m1.netname;
803 string m2 = _m2.netname;
804 string m3 = _m3.netname;
822 void WaypointSprite_UpdateHealth(entity e, float f)
824 f = bound(0, f, e.max_health);
825 if (f != e.health || e.pain_finished)
833 void WaypointSprite_UpdateMaxHealth(entity e, float f)
835 if (f != e.max_health || e.pain_finished)
843 void WaypointSprite_UpdateBuildFinished(entity e, float f)
845 if (f != e.pain_finished || e.max_health)
853 void WaypointSprite_UpdateOrigin(entity e, vector o)
862 void WaypointSprite_UpdateRule(entity e, float t, float r)
864 // no check, as this is never called without doing an actual change (usually only once)
870 void WaypointSprite_UpdateTeamRadar(entity e, entity icon, vector col)
872 // no check, as this is never called without doing an actual change (usually only once)
874 e.cnt = (e.cnt & BIT(7)) | (i & BITS(7));
879 void WaypointSprite_Ping(entity e)
882 if (time < e.waypointsprite_pingtime) return;
883 e.waypointsprite_pingtime = time + 0.3;
884 // ALWAYS sends (this causes a radar circle), thus no check
889 void WaypointSprite_HelpMePing(entity e)
891 WaypointSprite_Ping(e);
892 e.waypointsprite_helpmetime = time + waypointsprite_deployed_lifetime;
896 void WaypointSprite_FadeOutIn(entity e, float t)
901 e.teleport_time = time + t;
903 else if (t < (e.teleport_time - time))
905 // accelerate the waypoint's dying
907 // (e.teleport_time - time) / wp.fade_time stays
908 // e.teleport_time = time + fadetime
909 float current_fadetime = e.teleport_time - time;
910 e.teleport_time = time + t;
912 e.fade_time = -e.fade_time;
913 e.fade_time = e.fade_time * t / current_fadetime;
919 void WaypointSprite_Init()
921 waypointsprite_limitedrange = autocvar_sv_waypointsprite_limitedrange;
922 waypointsprite_deployed_lifetime = autocvar_sv_waypointsprite_deployed_lifetime;
923 waypointsprite_deadlifetime = autocvar_sv_waypointsprite_deadlifetime;
926 void WaypointSprite_Kill(entity wp)
929 if (wp.owner) wp.owner.(wp.owned_by_field) = NULL;
933 void WaypointSprite_Disown(entity wp, float fadetime)
936 if (wp.classname != "sprite_waypoint")
938 backtrace("Trying to disown a non-waypointsprite");
943 if (wp.exteriormodeltoclient == wp.owner)
944 wp.exteriormodeltoclient = NULL;
945 wp.owner.(wp.owned_by_field) = NULL;
948 WaypointSprite_FadeOutIn(wp, fadetime);
952 void WaypointSprite_Think(entity this)
954 bool doremove = false;
956 if (this.fade_time && time >= this.teleport_time)
961 if (this.exteriormodeltoclient)
962 WaypointSprite_UpdateOrigin(this, this.exteriormodeltoclient.origin + this.view_ofs);
965 WaypointSprite_Kill(this);
967 this.nextthink = time; // WHY?!?
970 bool WaypointSprite_visible_for_player(entity this, entity player, entity view)
972 // personal waypoints
973 if (this.enemy && this.enemy != view)
977 if (this.rule == SPRITERULE_SPECTATOR)
979 if (!autocvar_sv_itemstime)
981 if (!warmup_stage && IS_PLAYER(view) && autocvar_sv_itemstime != 2)
984 else if (this.team && this.rule == SPRITERULE_DEFAULT)
986 if (this.team != view.team)
988 if (!IS_PLAYER(view))
995 entity WaypointSprite_getviewentity(entity e)
997 if (IS_SPEC(e)) e = e.enemy;
998 /* TODO idea (check this breaks nothing)
999 else if (e.classname == "observer")
1005 float WaypointSprite_isteammate(entity e, entity e2)
1008 return e2.team == e.team;
1012 bool WaypointSprite_Customize(entity this, entity client)
1014 // this is not in SendEntity because it shall run every frame, not just every update
1016 // make spectators see what the player would see
1017 entity e = WaypointSprite_getviewentity(client);
1019 if (MUTATOR_CALLHOOK(CustomizeWaypoint, this, client))
1022 return this.waypointsprite_visible_for_player(this, client, e);
1025 bool WaypointSprite_SendEntity(entity this, entity to, float sendflags);
1027 void WaypointSprite_Reset(entity this)
1029 // if a WP wants to time out, let it time out immediately; other WPs ought to be reset/killed by their owners
1032 WaypointSprite_Kill(this);
1035 entity WaypointSprite_Spawn(
1036 entity spr, // sprite
1037 float _lifetime, float maxdistance, // lifetime, max distance
1038 entity ref, vector ofs, // position
1039 entity showto, float t, // show to whom? Use a flag to indicate a team
1040 entity own, .entity ownfield, // remove when own gets killed
1041 float hideable, // true when it should be controlled by cl_hidewaypoints
1042 entity icon // initial icon
1045 entity wp = new(sprite_waypoint);
1046 wp.fade_time = _lifetime; // if negative tells client not to fade it out
1048 _lifetime = -_lifetime;
1049 wp.teleport_time = time + _lifetime;
1050 wp.exteriormodeltoclient = ref;
1054 setorigin(wp, ref.origin + ofs);
1061 wp.currentammo = hideable;
1065 delete(own.(ownfield));
1066 own.(ownfield) = wp;
1067 wp.owned_by_field = ownfield;
1069 wp.fade_rate = maxdistance;
1070 setthink(wp, WaypointSprite_Think);
1071 wp.nextthink = time;
1072 wp.model1 = spr.netname;
1073 setcefc(wp, WaypointSprite_Customize);
1074 wp.waypointsprite_visible_for_player = WaypointSprite_visible_for_player;
1075 wp.reset2 = WaypointSprite_Reset;
1077 wp.colormod = spr.m_color;
1078 Net_LinkEntity(wp, false, 0, WaypointSprite_SendEntity);
1082 entity WaypointSprite_SpawnFixed(
1087 entity icon // initial icon
1090 return WaypointSprite_Spawn(spr, 0, 0, NULL, ofs, NULL, 0, own, ownfield, true, icon);
1093 entity WaypointSprite_DeployFixed(
1095 float limited_range,
1098 entity icon // initial icon
1108 maxdistance = waypointsprite_limitedrange;
1111 return WaypointSprite_Spawn(spr, waypointsprite_deployed_lifetime, maxdistance, NULL, ofs, NULL, t, player, waypointsprite_deployed_fixed, false, icon);
1114 entity WaypointSprite_DeployPersonal(
1118 entity icon // initial icon
1121 return WaypointSprite_Spawn(spr, 0, 0, NULL, ofs, NULL, 0, player, waypointsprite_deployed_personal, false, icon);
1124 entity WaypointSprite_Attach(
1127 float limited_range,
1128 entity icon // initial icon
1132 if (player.waypointsprite_attachedforcarrier)
1133 return NULL; // can't attach to FC
1140 maxdistance = waypointsprite_limitedrange;
1143 return WaypointSprite_Spawn(spr, waypointsprite_deployed_lifetime, maxdistance, player, '0 0 64', NULL, t, player, waypointsprite_attached, false, icon);
1146 entity WaypointSprite_AttachCarrier(
1149 entity icon // initial icon and color
1152 WaypointSprite_Kill(carrier.waypointsprite_attached); // FC overrides attached
1153 entity e = WaypointSprite_Spawn(spr, 0, 0, carrier, '0 0 64', NULL, carrier.team, carrier, waypointsprite_attachedforcarrier, false, icon);
1156 WaypointSprite_UpdateMaxHealth(e, '1 0 0' * healtharmor_maxdamage(start_health, start_armorvalue, autocvar_g_balance_armor_blockpercent, DEATH_WEAPON.m_id) * 2);
1157 WaypointSprite_UpdateHealth(e, '1 0 0' * healtharmor_maxdamage(carrier.health, carrier.armorvalue, autocvar_g_balance_armor_blockpercent, DEATH_WEAPON.m_id));
1162 void WaypointSprite_DetachCarrier(entity carrier)
1164 WaypointSprite_Disown(carrier.waypointsprite_attachedforcarrier, waypointsprite_deadlifetime);
1167 void WaypointSprite_ClearPersonal(entity this)
1169 WaypointSprite_Kill(this.waypointsprite_deployed_personal);
1172 void WaypointSprite_ClearOwned(entity this)
1174 WaypointSprite_Kill(this.waypointsprite_deployed_fixed);
1175 WaypointSprite_Kill(this.waypointsprite_deployed_personal);
1176 WaypointSprite_Kill(this.waypointsprite_attached);
1179 void WaypointSprite_PlayerDead(entity this)
1181 WaypointSprite_Disown(this.waypointsprite_attached, waypointsprite_deadlifetime);
1182 WaypointSprite_DetachCarrier(this);
1185 void WaypointSprite_PlayerGone(entity this)
1187 WaypointSprite_Disown(this.waypointsprite_deployed_fixed, waypointsprite_deadlifetime);
1188 WaypointSprite_Kill(this.waypointsprite_deployed_personal);
1189 WaypointSprite_Disown(this.waypointsprite_attached, waypointsprite_deadlifetime);
1190 WaypointSprite_DetachCarrier(this);