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 float txt_offset = 0;
701 draw_beginBoldFont();
702 if (this.health >= 0)
704 float align = 0, marg;
705 if (this.build_finished)
710 marg = -(SPRITE_HEALTHBAR_MARGIN + SPRITE_HEALTHBAR_HEIGHT + 2 * SPRITE_HEALTHBAR_BORDER) * t - 0.5 * waypointsprite_fontsize;
712 marg = SPRITE_HEALTHBAR_MARGIN * t + 0.5 * waypointsprite_fontsize;
719 SPRITE_HEALTHBAR_WIDTH * t,
720 SPRITE_HEALTHBAR_HEIGHT * t,
722 SPRITE_HEALTHBAR_BORDER * t,
725 a * SPRITE_HEALTHBAR_BORDERALPHA,
727 a * SPRITE_HEALTHBAR_HEALTHALPHA,
731 txt_offset = (SPRITE_HEALTHBAR_WIDTH + 2 * SPRITE_HEALTHBAR_BORDER) * t;
732 icon_offset = vec2(autocvar_g_waypointsprite_iconsize/2, autocvar_g_waypointsprite_iconsize*t + 2*marg + SPRITE_HEALTHBAR_HEIGHT*t);
735 icon_offset = vec2(autocvar_g_waypointsprite_iconsize/2, autocvar_g_waypointsprite_iconsize*t + 2 + SPRITE_HEALTHBAR_HEIGHT*t);
737 if (autocvar_g_waypointsprite_text || !icon_found)
738 o = drawspritetext(o, ang, txt_offset, rgb, a, waypointsprite_fontsize * '1 1 0', txt);
740 drawpic(o - icon_offset, pic, '1 1 0'*autocvar_g_waypointsprite_iconsize, iconcolor, a, DRAWFLAG_NORMAL);
745 void WaypointSprite_Load_Frames(string ext)
747 int dh = search_begin(strcat("models/sprites/*_frame*", ext), false, false);
749 int ext_len = strlen(ext);
750 int n = search_getsize(dh);
751 for (int i = 0; i < n; ++i)
753 string s = search_getfilename(dh, i);
754 s = substring(s, 15, strlen(s) - 15 - ext_len); // strip models/sprites/ and extension
756 int o = strstrofs(s, "_frame", 0);
757 string sname = strcat("/spriteframes/", substring(s, 0, o));
758 string sframes = substring(s, o + 6, strlen(s) - o - 6);
759 int f = stof(sframes) + 1;
760 db_put(tempdb, sname, ftos(max(f, stof(db_get(tempdb, sname)))));
765 void WaypointSprite_Load();
766 STATIC_INIT(WaypointSprite_Load) {
767 WaypointSprite_Load();
768 WaypointSprite_Load_Frames(".tga");
769 WaypointSprite_Load_Frames(".jpg");
771 void WaypointSprite_Load()
773 waypointsprite_fadedistance = vlen(mi_scale);
774 waypointsprite_normdistance = autocvar_g_waypointsprite_normdistance;
775 waypointsprite_minscale = autocvar_g_waypointsprite_minscale;
776 waypointsprite_minalpha = autocvar_g_waypointsprite_minalpha;
777 waypointsprite_distancealphaexponent = autocvar_g_waypointsprite_distancealphaexponent;
778 waypointsprite_timealphaexponent = autocvar_g_waypointsprite_timealphaexponent;
779 waypointsprite_scale = autocvar_g_waypointsprite_scale;
780 waypointsprite_fontsize = autocvar_g_waypointsprite_fontsize;
781 waypointsprite_edgefadealpha = autocvar_g_waypointsprite_edgefadealpha;
782 waypointsprite_edgefadescale = autocvar_g_waypointsprite_edgefadescale;
783 waypointsprite_edgefadedistance = autocvar_g_waypointsprite_edgefadedistance;
784 waypointsprite_edgeoffset_bottom = autocvar_g_waypointsprite_edgeoffset_bottom;
785 waypointsprite_edgeoffset_left = autocvar_g_waypointsprite_edgeoffset_left;
786 waypointsprite_edgeoffset_right = autocvar_g_waypointsprite_edgeoffset_right;
787 waypointsprite_edgeoffset_top = autocvar_g_waypointsprite_edgeoffset_top;
788 waypointsprite_crosshairfadealpha = autocvar_g_waypointsprite_crosshairfadealpha;
789 waypointsprite_crosshairfadescale = autocvar_g_waypointsprite_crosshairfadescale;
790 waypointsprite_crosshairfadedistance = autocvar_g_waypointsprite_crosshairfadedistance;
791 waypointsprite_distancefadealpha = autocvar_g_waypointsprite_distancefadealpha;
792 waypointsprite_distancefadescale = autocvar_g_waypointsprite_distancefadescale;
793 waypointsprite_distancefadedistance = waypointsprite_fadedistance * autocvar_g_waypointsprite_distancefadedistancemultiplier;
794 waypointsprite_alpha = autocvar_g_waypointsprite_alpha * (1 - autocvar__menu_alpha);
796 waypointsprite_count = waypointsprite_newcount;
797 waypointsprite_newcount = 0;
802 void WaypointSprite_UpdateSprites(entity e, entity _m1, entity _m2, entity _m3)
804 string m1 = _m1.netname;
805 string m2 = _m2.netname;
806 string m3 = _m3.netname;
824 void WaypointSprite_UpdateHealth(entity e, float f)
826 f = bound(0, f, e.max_health);
827 if (f != e.health || e.pain_finished)
835 void WaypointSprite_UpdateMaxHealth(entity e, float f)
837 if (f != e.max_health || e.pain_finished)
845 void WaypointSprite_UpdateBuildFinished(entity e, float f)
847 if (f != e.pain_finished || e.max_health)
855 void WaypointSprite_UpdateOrigin(entity e, vector o)
864 void WaypointSprite_UpdateRule(entity e, float t, float r)
866 // no check, as this is never called without doing an actual change (usually only once)
872 void WaypointSprite_UpdateTeamRadar(entity e, entity icon, vector col)
874 // no check, as this is never called without doing an actual change (usually only once)
876 e.cnt = (e.cnt & BIT(7)) | (i & BITS(7));
881 void WaypointSprite_Ping(entity e)
884 if (time < e.waypointsprite_pingtime) return;
885 e.waypointsprite_pingtime = time + 0.3;
886 // ALWAYS sends (this causes a radar circle), thus no check
891 void WaypointSprite_HelpMePing(entity e)
893 WaypointSprite_Ping(e);
894 e.waypointsprite_helpmetime = time + waypointsprite_deployed_lifetime;
898 void WaypointSprite_FadeOutIn(entity e, float t)
903 e.teleport_time = time + t;
905 else if (t < (e.teleport_time - time))
907 // accelerate the waypoint's dying
909 // (e.teleport_time - time) / wp.fade_time stays
910 // e.teleport_time = time + fadetime
911 float current_fadetime = e.teleport_time - time;
912 e.teleport_time = time + t;
914 e.fade_time = -e.fade_time;
915 e.fade_time = e.fade_time * t / current_fadetime;
921 void WaypointSprite_Init()
923 waypointsprite_limitedrange = autocvar_sv_waypointsprite_limitedrange;
924 waypointsprite_deployed_lifetime = autocvar_sv_waypointsprite_deployed_lifetime;
925 waypointsprite_deadlifetime = autocvar_sv_waypointsprite_deadlifetime;
928 void WaypointSprite_Kill(entity wp)
931 if (wp.owner) wp.owner.(wp.owned_by_field) = NULL;
935 void WaypointSprite_Disown(entity wp, float fadetime)
938 if (wp.classname != "sprite_waypoint")
940 backtrace("Trying to disown a non-waypointsprite");
945 if (wp.exteriormodeltoclient == wp.owner)
946 wp.exteriormodeltoclient = NULL;
947 wp.owner.(wp.owned_by_field) = NULL;
950 WaypointSprite_FadeOutIn(wp, fadetime);
954 void WaypointSprite_Think(entity this)
956 bool doremove = false;
958 if (this.fade_time && time >= this.teleport_time)
963 if (this.exteriormodeltoclient)
964 WaypointSprite_UpdateOrigin(this, this.exteriormodeltoclient.origin + this.view_ofs);
967 WaypointSprite_Kill(this);
969 this.nextthink = time; // WHY?!?
972 bool WaypointSprite_visible_for_player(entity this, entity player, entity view)
974 // personal waypoints
975 if (this.enemy && this.enemy != view)
979 if (this.rule == SPRITERULE_SPECTATOR)
981 if (!autocvar_sv_itemstime)
983 if (!warmup_stage && IS_PLAYER(view) && autocvar_sv_itemstime != 2)
986 else if (this.team && this.rule == SPRITERULE_DEFAULT)
988 if (this.team != view.team)
990 if (!IS_PLAYER(view))
997 entity WaypointSprite_getviewentity(entity e)
999 if (IS_SPEC(e)) e = e.enemy;
1000 /* TODO idea (check this breaks nothing)
1001 else if (e.classname == "observer")
1007 float WaypointSprite_isteammate(entity e, entity e2)
1010 return e2.team == e.team;
1014 bool WaypointSprite_Customize(entity this, entity client)
1016 // this is not in SendEntity because it shall run every frame, not just every update
1018 // make spectators see what the player would see
1019 entity e = WaypointSprite_getviewentity(client);
1021 if (MUTATOR_CALLHOOK(CustomizeWaypoint, this, client))
1024 return this.waypointsprite_visible_for_player(this, client, e);
1027 bool WaypointSprite_SendEntity(entity this, entity to, float sendflags);
1029 void WaypointSprite_Reset(entity this)
1031 // if a WP wants to time out, let it time out immediately; other WPs ought to be reset/killed by their owners
1034 WaypointSprite_Kill(this);
1037 entity WaypointSprite_Spawn(
1038 entity spr, // sprite
1039 float _lifetime, float maxdistance, // lifetime, max distance
1040 entity ref, vector ofs, // position
1041 entity showto, float t, // show to whom? Use a flag to indicate a team
1042 entity own, .entity ownfield, // remove when own gets killed
1043 float hideable, // true when it should be controlled by cl_hidewaypoints
1044 entity icon // initial icon
1047 entity wp = new(sprite_waypoint);
1048 wp.fade_time = _lifetime; // if negative tells client not to fade it out
1050 _lifetime = -_lifetime;
1051 wp.teleport_time = time + _lifetime;
1052 wp.exteriormodeltoclient = ref;
1056 setorigin(wp, ref.origin + ofs);
1063 wp.currentammo = hideable;
1067 delete(own.(ownfield));
1068 own.(ownfield) = wp;
1069 wp.owned_by_field = ownfield;
1071 wp.fade_rate = maxdistance;
1072 setthink(wp, WaypointSprite_Think);
1073 wp.nextthink = time;
1074 wp.model1 = spr.netname;
1075 setcefc(wp, WaypointSprite_Customize);
1076 wp.waypointsprite_visible_for_player = WaypointSprite_visible_for_player;
1077 wp.reset2 = WaypointSprite_Reset;
1079 wp.colormod = spr.m_color;
1080 Net_LinkEntity(wp, false, 0, WaypointSprite_SendEntity);
1084 entity WaypointSprite_SpawnFixed(
1089 entity icon // initial icon
1092 return WaypointSprite_Spawn(spr, 0, 0, NULL, ofs, NULL, 0, own, ownfield, true, icon);
1095 entity WaypointSprite_DeployFixed(
1097 float limited_range,
1100 entity icon // initial icon
1110 maxdistance = waypointsprite_limitedrange;
1113 return WaypointSprite_Spawn(spr, waypointsprite_deployed_lifetime, maxdistance, NULL, ofs, NULL, t, player, waypointsprite_deployed_fixed, false, icon);
1116 entity WaypointSprite_DeployPersonal(
1120 entity icon // initial icon
1123 return WaypointSprite_Spawn(spr, 0, 0, NULL, ofs, NULL, 0, player, waypointsprite_deployed_personal, false, icon);
1126 entity WaypointSprite_Attach(
1129 float limited_range,
1130 entity icon // initial icon
1134 if (player.waypointsprite_attachedforcarrier)
1135 return NULL; // can't attach to FC
1142 maxdistance = waypointsprite_limitedrange;
1145 return WaypointSprite_Spawn(spr, waypointsprite_deployed_lifetime, maxdistance, player, '0 0 64', NULL, t, player, waypointsprite_attached, false, icon);
1148 entity WaypointSprite_AttachCarrier(
1151 entity icon // initial icon and color
1154 WaypointSprite_Kill(carrier.waypointsprite_attached); // FC overrides attached
1155 entity e = WaypointSprite_Spawn(spr, 0, 0, carrier, '0 0 64', NULL, carrier.team, carrier, waypointsprite_attachedforcarrier, false, icon);
1158 WaypointSprite_UpdateMaxHealth(e, '1 0 0' * healtharmor_maxdamage(start_health, start_armorvalue, autocvar_g_balance_armor_blockpercent, DEATH_WEAPON.m_id) * 2);
1159 WaypointSprite_UpdateHealth(e, '1 0 0' * healtharmor_maxdamage(carrier.health, carrier.armorvalue, autocvar_g_balance_armor_blockpercent, DEATH_WEAPON.m_id));
1164 void WaypointSprite_DetachCarrier(entity carrier)
1166 WaypointSprite_Disown(carrier.waypointsprite_attachedforcarrier, waypointsprite_deadlifetime);
1169 void WaypointSprite_ClearPersonal(entity this)
1171 WaypointSprite_Kill(this.waypointsprite_deployed_personal);
1174 void WaypointSprite_ClearOwned(entity this)
1176 WaypointSprite_Kill(this.waypointsprite_deployed_fixed);
1177 WaypointSprite_Kill(this.waypointsprite_deployed_personal);
1178 WaypointSprite_Kill(this.waypointsprite_attached);
1181 void WaypointSprite_PlayerDead(entity this)
1183 WaypointSprite_Disown(this.waypointsprite_attached, waypointsprite_deadlifetime);
1184 WaypointSprite_DetachCarrier(this);
1187 void WaypointSprite_PlayerGone(entity this)
1189 WaypointSprite_Disown(this.waypointsprite_deployed_fixed, waypointsprite_deadlifetime);
1190 WaypointSprite_Kill(this.waypointsprite_deployed_personal);
1191 WaypointSprite_Disown(this.waypointsprite_attached, waypointsprite_deadlifetime);
1192 WaypointSprite_DetachCarrier(this);