Xonotic QuakeC
The free, fast arena FPS with crisp movement and a wide array of weapons
waypointsprites.qc
Go to the documentation of this file.
1#include "waypointsprites.qh"
2
3#ifdef GAMEQC
4REGISTER_MUTATOR(waypointsprites, true);
5REGISTER_NET_LINKED(waypointsprites)
6#endif
7
8#ifdef SVQC
9bool WaypointSprite_SendEntity(entity this, entity to, float sendflags)
10{
11 WriteHeader(MSG_ENTITY, waypointsprites);
12
13 sendflags &= 0x7F;
14
15 if (this.max_health || (this.pain_finished && (time < this.pain_finished + 0.25)))
16 sendflags |= 0x80;
17
18 int hide_flags = 0;
19 if(this.currentammo == 1) hide_flags |= 1; // hideable
20 else if(this.currentammo == 2) hide_flags |= 2; // radar only
21 if(this.exteriormodeltoclient == to) hide_flags |= 2; // my own
22
23 MUTATOR_CALLHOOK(SendWaypoint, this, to, sendflags, hide_flags);
24 sendflags = M_ARGV(2, int);
25 hide_flags = M_ARGV(3, int);
26
27 WriteByte(MSG_ENTITY, sendflags);
29
30 if (sendflags & 0x80)
31 {
32 if (this.max_health)
33 WriteByte(MSG_ENTITY, (GetResource(this, RES_HEALTH) / this.max_health) * 191.0);
34 else
35 {
36 float dt = this.pain_finished - time;
37 dt = bound(0, dt * 32, 16383);
38 WriteByte(MSG_ENTITY, (dt & 0xFF00) / 256 + 192);
39 WriteByte(MSG_ENTITY, (dt & 0x00FF));
40 }
41 }
42
43 if (sendflags & 64)
44 WriteVector(MSG_ENTITY, this.origin);
45
46 if (sendflags & 1)
47 {
50 }
51
52 if (sendflags & 2)
54
55 if (sendflags & 4)
57
58 if (sendflags & 8)
60
61 if (sendflags & 16)
62 {
65 WriteShort(MSG_ENTITY, bound(0, this.fade_rate, 32767)); // maxdist
66 WriteByte(MSG_ENTITY, hide_flags);
67 }
68
69 if (sendflags & 32)
70 {
71 WriteByte(MSG_ENTITY, this.cnt); // icon on radar
72 WriteByte(MSG_ENTITY, this.colormod.x * 255.0);
73 WriteByte(MSG_ENTITY, this.colormod.y * 255.0);
74 WriteByte(MSG_ENTITY, this.colormod.z * 255.0);
75
77 {
78 float dt = bound(0, (this.waypointsprite_helpmetime - time) / 0.1, 255);
80 }
81 else
83 }
84
85 return true;
86}
87#endif
88
89#ifdef CSQC
90void Ent_WaypointSprite(entity this, bool isnew);
91NET_HANDLE(waypointsprites, bool isnew)
92{
93 Ent_WaypointSprite(this, isnew);
94 return true;
95}
96
98{
99 strfree(this.netname);
100 strfree(this.netname2);
101 strfree(this.netname3);
102}
103
104void Ent_WaypointSprite(entity this, bool isnew)
105{
106 int sendflags = ReadByte();
107 this.wp_extra = ReadByte();
108
109 if (!this.spawntime)
110 this.spawntime = time;
111
112 this.draw2d = Draw_WaypointSprite;
113 if (isnew) {
114 IL_PUSH(g_drawables_2d, this);
115 IL_PUSH(g_radaricons, this);
116 }
117
119 this.iflags |= IFLAG_ORIGIN;
120
121 if (sendflags & 0x80)
122 {
123 int t = ReadByte();
124 if (t < 192)
125 {
126 SetResourceExplicit(this, RES_HEALTH, t / 191.0);
127 this.build_finished = 0;
128 }
129 else
130 {
131 t = (t - 192) * 256 + ReadByte();
132 this.build_started = servertime;
133 if (this.build_finished)
134 this.build_starthealth = bound(0, GetResource(this, RES_HEALTH), 1);
135 else
136 this.build_starthealth = 0;
137 this.build_finished = servertime + t / 32;
138 }
139 }
140 else
141 {
142 SetResourceExplicit(this, RES_HEALTH, -1);
143 this.build_finished = 0;
144 }
145
146 if (sendflags & 64)
147 {
148 // unfortunately, this needs to be exact (for the 3D display)
149 this.origin = ReadVector();
150 setorigin(this, this.origin);
151 }
152
153 if (sendflags & 1)
154 {
155 this.team = ReadByte();
156 this.rule = ReadByte();
157 }
158
159 if (sendflags & 2)
160 strcpy(this.netname, ReadString());
161
162 if (sendflags & 4)
163 strcpy(this.netname2, ReadString());
164
165 if (sendflags & 8)
166 strcpy(this.netname3, ReadString());
167
168 if (sendflags & 16)
169 {
170 this.lifetime = ReadCoord();
171 this.fadetime = ReadCoord();
172 this.maxdistance = ReadShort();
173 this.hideflags = ReadByte();
174 }
175
176 if (sendflags & 32)
177 {
178 int f = ReadByte();
179 this.teamradar_icon = f & BITS(7);
180 if (f & BIT(7))
181 {
182 this.(teamradar_times[this.teamradar_time_index]) = time;
183 this.teamradar_time_index = (this.teamradar_time_index + 1) % MAX_TEAMRADAR_TIMES;
184 }
185 this.teamradar_color_x = ReadByte() / 255.0;
186 this.teamradar_color_y = ReadByte() / 255.0;
187 this.teamradar_color_z = ReadByte() / 255.0;
188 this.helpme = ReadByte() * 0.1;
189 if (this.helpme > 0)
190 this.helpme += servertime;
191 }
192
194
195 this.entremove = Ent_RemoveWaypointSprite;
196}
197#endif
198
199#ifdef CSQC
200float spritelookupblinkvalue(entity this, string s)
201{
202 if (s == WP_Weapon.netname) {
204 return 2;
205 }
206 if (s == WP_Item.netname) return REGISTRY_GET(Items, this.wp_extra).m_waypointblink;
207 if(s == WP_FlagReturn.netname) return 2;
208
209 return 1;
210}
211
213{
214 if (s == WP_Weapon.netname || s == RADARICON_Weapon.netname) return REGISTRY_GET(Weapons, this.wp_extra).m_color;
215 if (s == WP_Item.netname || s == RADARICON_Item.netname) return REGISTRY_GET(Items, this.wp_extra).m_color;
216 if (MUTATOR_CALLHOOK(WP_Format, this, s))
217 {
218 return M_ARGV(2, vector);
219 }
220 return def;
221}
222
223string spritelookuptext(entity this, string s)
224{
226 return "Spam"; // no need to translate this debug string
227 if (s == WP_RaceStartFinish.netname) return (race_checkpointtime || race_mycheckpointtime) ? _("Finish") : _("Start");
228 if (s == WP_Weapon.netname) return REGISTRY_GET(Weapons, this.wp_extra).m_name;
229 if (s == WP_Item.netname) return REGISTRY_GET(Items, this.wp_extra).m_waypoint;
230 if (s == WP_Monster.netname) return get_monsterinfo(this.wp_extra).m_name;
231 if (MUTATOR_CALLHOOK(WP_Format, this, s))
232 {
233 return M_ARGV(3, string);
234 }
235
236 // need to loop, as our netname could be one of three
237 FOREACH(Waypoints, it.netname == s, {
238 return it.m_name;
239 });
240
241 return s;
242}
243
244string spritelookupicon(entity this, string s)
245{
246 // TODO: needs icons! //if (s == WP_RaceStartFinish.netname) return (race_checkpointtime || race_mycheckpointtime) ? _("Finish") : _("Start");
247 if (s == WP_Weapon.netname) return REGISTRY_GET(Weapons, this.wp_extra).model2;
248 if (s == WP_Item.netname) return REGISTRY_GET(Items, this.wp_extra).m_icon;
249 if (s == WP_Vehicle.netname) return REGISTRY_GET(Vehicles, this.wp_extra).m_icon;
250 //if (s == WP_Monster.netname) return get_monsterinfo(this.wp_extra).m_icon;
251 if (MUTATOR_CALLHOOK(WP_Format, this, s))
252 {
253 return M_ARGV(4, string);
254 }
255
256 // need to loop, as our netname could be one of three
257 FOREACH(Waypoints, it.netname == s, {
258 return it.m_icon;
259 });
260
261 return s;
262}
263#endif
264
265#ifdef CSQC
266void drawrotpic(vector org, float rot, string pic, vector sz, vector hotspot, vector rgb, float a, float f)
267{
268 vector v1, v2, v3, v4;
269
270 hotspot = -1 * hotspot;
271
272 // hotspot-relative coordinates of the corners
273 v1 = hotspot;
274 v2 = hotspot + '1 0 0' * sz.x;
275 v3 = hotspot + '1 0 0' * sz.x + '0 1 0' * sz.y;
276 v4 = hotspot + '0 1 0' * sz.y;
277
278 // rotate them, and make them absolute
279 rot = -rot; // rotate by the opposite angle, as our coordinate system is reversed
280 v1 = Rotate(v1, rot) + org;
281 v2 = Rotate(v2, rot) + org;
282 v3 = Rotate(v3, rot) + org;
283 v4 = Rotate(v4, rot) + org;
284
285 // draw them
286 R_BeginPolygon(pic, f, true);
287 R_PolygonVertex(v1, '0 0 0', rgb, a);
288 R_PolygonVertex(v2, '1 0 0', rgb, a);
289 R_PolygonVertex(v3, '1 1 0', rgb, a);
290 R_PolygonVertex(v4, '0 1 0', rgb, a);
291 R_EndPolygon();
292}
293
294void drawquad(vector o, vector ri, vector up, string pic, vector rgb, float a, float f)
295{
296 R_BeginPolygon(pic, f, true);
297 R_PolygonVertex(o, '0 0 0', rgb, a);
298 R_PolygonVertex(o + ri, '1 0 0', rgb, a);
299 R_PolygonVertex(o + up + ri, '1 1 0', rgb, a);
300 R_PolygonVertex(o + up, '0 1 0', rgb, a);
301 R_EndPolygon();
302}
303
304void 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)
305{
306 vector o, ri, up;
307 float owidth; // outer width
308
309 hotspot = -1 * hotspot;
310
311 // hotspot-relative coordinates of the healthbar corners
312 o = hotspot;
313 ri = '1 0 0';
314 up = '0 1 0';
315
316 rot = -rot; // rotate by the opposite angle, as our coordinate system is reversed
317 o = Rotate(o, rot) + org;
318 ri = Rotate(ri, rot);
319 up = Rotate(up, rot);
320
321 owidth = width + 2 * border;
322 o += up * -(margin + border + theheight) + ri * (sz.x - owidth) * 0.5;
323
324 drawquad(o - up * border, ri * owidth, up * border, "", rgb, a, f);
325 drawquad(o + up * theheight, ri * owidth, up * border, "", rgb, a, f);
326 drawquad(o, ri * border, up * theheight, "", rgb, a, f);
327 drawquad(o + ri * (owidth - border), ri * border, up * theheight, "", rgb, a, f);
328 drawquad(o + ri * (border + align * ((1 - h) * width)), ri * width * h, up * theheight, "", hrgb, ha, f);
329}
330
331// returns location of sprite text
332vector drawspritearrow(vector o, float ang, vector rgb, float a, float t)
333{
334 float size = 9.0 * t;
335 float border = 1.5 * t;
336 float margin = 4.0 * t;
337
338 float borderDiag = border * M_SQRT2;
339 vector arrowX = eX * size;
340 vector arrowY = eY * (size+borderDiag);
341 vector borderX = eX * (size+borderDiag);
342 vector borderY = eY * (size+borderDiag+border);
343
344 R_BeginPolygon("", DRAWFLAG_NORMAL, true);
345 R_PolygonVertex(o, '0 0 0', '0 0 0', a);
346 R_PolygonVertex(o + Rotate(arrowY - borderX, ang), '0 0 0', '0 0 0', a);
347 R_PolygonVertex(o + Rotate(borderY - borderX, ang), '0 0 0', '0 0 0', a);
348 R_PolygonVertex(o + Rotate(borderY + borderX, ang), '0 0 0', '0 0 0', a);
349 R_PolygonVertex(o + Rotate(arrowY + borderX, ang), '0 0 0', '0 0 0', a);
350 R_EndPolygon();
351
352 R_BeginPolygon("", DRAWFLAG_ADDITIVE, true);
353 R_PolygonVertex(o + Rotate(eY * borderDiag, ang), '0 0 0', rgb, a);
354 R_PolygonVertex(o + Rotate(arrowY - arrowX, ang), '0 0 0', rgb, a);
355 R_PolygonVertex(o + Rotate(arrowY + arrowX, ang), '0 0 0', rgb, a);
356 R_EndPolygon();
357
358 return o + Rotate(eY * (borderDiag+size+margin), ang);
359}
360
361// returns location of sprite healthbar
362vector drawsprite_TextOrIcon(bool is_text, vector o, float ang, float minwidth, vector rgb, float a, vector sz, string str)
363{
364 float algnx, algny;
365
366 float sw = is_text ? stringwidth(str, false, sz) : sz.x;
367 float w = sw > minwidth ? sw : minwidth;
368 float h = sz.y;
369
370 // how do corners work?
371 float aspect = vid_conwidth / vid_conheight;
372 float sa = sin(ang);
373 float ca = cos(ang) * aspect;
374 if (fabs(sa) > fabs(ca))
375 {
376 algnx = (sa < 0);
377 float f = fabs(sa);
378 algny = 0.5 - 0.5 * (f ? (ca / f) : 0);
379 }
380 else
381 {
382 float f = fabs(ca);
383 algnx = 0.5 - 0.5 * (f ? (sa / f) : 0);
384 algny = (ca < 0);
385 }
386
387 // align
388 o.x -= w * algnx;
389 o.y -= h * algny;
390
391 // we want to be onscreen
392 if (o.x < 0)
393 o.x = 0;
394 if (o.y < 0)
395 o.y = 0;
396 if (o.x > vid_conwidth - w)
397 o.x = vid_conwidth - w;
398 if (o.y > vid_conheight - h)
399 o.y = vid_conheight - h;
400
401 o.x += 0.5 * (w - sw);
402
403 if (is_text)
404 drawstring(o, str, sz, rgb, a, DRAWFLAG_NORMAL);
405 else
406 drawpic_aspect(o, str, sz, rgb, a, DRAWFLAG_NORMAL);
407
408 o.x += 0.5 * sw;
409 o.y += 0.5 * h;
410
411 return o;
412}
413
415{
416 vector yvec = '0.299 0.587 0.114';
417 return rgb + dst * ((src * yvec) / (dst * yvec)) * ((rgb - '1 1 1') * src);
418}
419
421{
422 if (rgb.x > 1) {
423 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 1 1');
424 if (rgb.y > 1) {
425 rgb = fixrgbexcess_move(rgb, '0 1 0', '0 0 1');
426 if (rgb.z > 1) rgb.z = 1;
427 } else if (rgb.z > 1) {
428 rgb = fixrgbexcess_move(rgb, '0 0 1', '0 1 0');
429 if (rgb.y > 1) rgb.y = 1;
430 }
431 } else if (rgb.y > 1) {
432 rgb = fixrgbexcess_move(rgb, '0 1 0', '1 0 1');
433 if (rgb.x > 1) {
434 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 0 1');
435 if (rgb.z > 1) rgb.z = 1;
436 } else if (rgb.z > 1) {
437 rgb = fixrgbexcess_move(rgb, '0 0 1', '1 0 0');
438 if (rgb.x > 1) rgb.x = 1;
439 }
440 } else if (rgb.z > 1) {
441 rgb = fixrgbexcess_move(rgb, '0 0 1', '1 1 0');
442 if (rgb.x > 1) {
443 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 1 0');
444 if (rgb.y > 1) rgb.y = 1;
445 } else if (rgb.y > 1) {
446 rgb = fixrgbexcess_move(rgb, '0 1 0', '1 0 0');
447 if (rgb.x > 1) rgb.x = 1;
448 }
449 }
450 return rgb;
451}
452
454{
455 if (this.lifetime > 0)
456 this.alpha = (bound(0, (this.fadetime - time) / this.lifetime, 1) ** waypointsprite_timealphaexponent);
457 else
458 this.alpha = 1;
459
460 if (this.hideflags & 2)
461 return; // radar only
462
464 return;
465
466 if ((this.hideflags & 1) && autocvar_cl_hidewaypoints)
467 return; // fixed waypoint
468
470
471 float t = entcs_GetTeam(player_localnum) + 1;
472 string spriteimage = "";
473
474 // choose the sprite
475 switch (this.rule)
476 {
478 if (!(
480 || (autocvar_g_waypointsprite_itemstime == 2 && (t == NUM_SPECTATOR + 1 || warmup_stage || STAT(ITEMSTIME) == 2))
481 ))
482 return;
483 spriteimage = this.netname;
484 break;
486 if (this.team)
487 {
488 if (this.team == t)
489 spriteimage = this.netname;
490 else
491 spriteimage = "";
492 }
493 else
494 spriteimage = this.netname;
495 break;
497 if (t == NUM_SPECTATOR + 1)
498 spriteimage = this.netname3;
499 else if (this.team == t)
500 spriteimage = this.netname2;
501 else
502 spriteimage = this.netname;
503 break;
504 default:
505 error("Invalid waypointsprite rule!");
506 break;
507 }
508
509 if (spriteimage == "")
510 return;
511
513
514 float dist = vlen(this.origin - view_origin);
515 float a = this.alpha * autocvar_hud_panel_fg_alpha;
516
517 if(this.maxdistance > 0)
518 {
519 // restrict maximum normal distance to the waypoint's maximum distance to prevent exploiting cvars
520 float maxnormdistance = bound(0, waypointsprite_normdistance, this.maxdistance - 1);
521 a *= (bound(0, (this.maxdistance - dist) / (this.maxdistance - maxnormdistance), 1) ** waypointsprite_distancealphaexponent);
522 }
523
524 vector rgb = spritelookupcolor(this, spriteimage, this.teamradar_color);
525 if (rgb == '0 0 0')
526 {
527 this.teamradar_color = '1 0 1';
528 LOG_INFOF("WARNING: sprite of name %s has no color, using pink so you notice it", spriteimage);
529 }
530
531 float health_val = GetResource(this, RES_HEALTH);
532 float blink_time = (health_val >= 0) ? (health_val * 10) : time;
533 if (blink_time - floor(blink_time) > 0.5)
534 {
535 if (this.helpme && time < this.helpme)
537 else if (!this.lifetime) // fading out waypoints don't blink
538 a *= spritelookupblinkvalue(this, spriteimage);
539 }
540
541 if (a > 1)
542 {
543 rgb *= a;
544 a = 1;
545 }
546
547 if (a <= 0.003)
548 return;
549
550 rgb = fixrgbexcess(rgb);
551
552 vector o;
553 float ang;
554
555 o = project_3d_to_2d(this.origin);
556 if (o.z < 0
561 {
562 // scale it to be just in view
563 vector d = o - '0.5 0 0' * vid_conwidth - '0 0.5 0' * vid_conheight;
564 ang = atan2(-d.x, -d.y);
565 if (o.z < 0)
566 ang += M_PI;
567
568 float f1 = d.x / vid_conwidth;
569 float f2 = d.y / vid_conheight;
570 if (f1 == 0) { f1 = 0.000001; }
571 if (f2 == 0) { f2 = 0.000001; }
572
573 if (max(f1, -f1) > max(f2, -f2)) {
574 if (d.z * f1 > 0) {
575 // RIGHT edge
576 d *= (0.5 - waypointsprite_edgeoffset_right) / f1;
577 } else {
578 // LEFT edge
579 d *= -(0.5 - waypointsprite_edgeoffset_left) / f1;
580 }
581 } else {
582 if (d.z * f2 > 0) {
583 // BOTTOM edge
585 } else {
586 // TOP edge
587 d *= -(0.5 - waypointsprite_edgeoffset_top) / f2;
588 }
589 }
590
591 o = d + '0.5 0 0' * vid_conwidth + '0 0.5 0' * vid_conheight;
592 }
593 else
594 {
595#if 1
596 ang = M_PI;
597#else
598 vector d = o - '0.5 0 0' * vid_conwidth - '0 0.5 0' * vid_conheight;
599 ang = atan2(-d.x, -d.y);
600#endif
601 }
602 o.z = 0;
603
604 float edgedistance_min = min((o.y - (vid_conheight * waypointsprite_edgeoffset_top)),
608
609 float crosshairdistance = sqrt( ((o.x - vid_conwidth/2) ** 2) + ((o.y - vid_conheight/2) ** 2) );
610
613
614 {
617 }
618 if (edgedistance_min < waypointsprite_edgefadedistance) {
619 a *= 1 - (1 - waypointsprite_edgefadealpha) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1));
620 t *= 1 - (1 - waypointsprite_edgefadescale) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1));
621 }
622 if (crosshairdistance < waypointsprite_crosshairfadedistance) {
623 a *= 1 - (1 - waypointsprite_crosshairfadealpha) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1));
624 t *= 1 - (1 - waypointsprite_crosshairfadescale) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1));
625 }
626
627 if (this.build_finished)
628 {
629 if (time < this.build_finished + 0.25)
630 {
631 if (time < this.build_started)
632 SetResourceExplicit(this, RES_HEALTH, this.build_starthealth);
633 else if (time < this.build_finished)
634 SetResourceExplicit(this, RES_HEALTH, (time - this.build_started) / (this.build_finished - this.build_started) * (1 - this.build_starthealth) + this.build_starthealth);
635 else
636 SetResourceExplicit(this, RES_HEALTH, 1);
637 }
638 else
639 SetResourceExplicit(this, RES_HEALTH, -1);
640 }
641
642 o = drawspritearrow(o, ang, rgb, a, SPRITE_ARROW_SCALE * t);
643
644 string pic = "";
645 bool is_text = true;
647 {
648 string spr_icon = spritelookupicon(this, spriteimage);
649 pic = spr_icon;
650 bool icon_found = !(!spr_icon || spr_icon == "");
651 if (icon_found) // it's valid, but let's make sure it exists!
652 {
653 pic = strcat(hud_skin_path, "/", spr_icon);
654 if(precache_pic(pic) == "")
655 {
656 pic = strcat("gfx/hud/default/", spr_icon);
657 if(!precache_pic(pic))
658 icon_found = false;
659 }
660 }
661 if (icon_found)
662 is_text = false;
663 }
664
665 vector sz;
666 vector col = rgb;
667 string txt = string_null; // it will contain either the text or the icon path
668 if (is_text)
669 {
670 txt = spritelookuptext(this, spriteimage);
671 if (this.helpme && time < this.helpme)
672 txt = sprintf(_("%s needing help!"), txt);
674 txt = strtoupper(txt);
675 sz = waypointsprite_fontsize * '1 1 0';
676 }
677 else
678 {
679 txt = pic; // icon path
681 col = '1 1 1';
683 {
684 col = rgb_to_hsv(col);
685 col.y *= autocvar_g_waypointsprite_iconcolor; // scale saturation
686 col = hsv_to_rgb(col);
687 }
689 }
690
692 if (GetResource(this, RES_HEALTH) >= 0)
693 {
694 float align = 0, marg;
695 if (this.build_finished)
696 align = 0.5;
697 else
698 align = 0;
699 if (cos(ang) > 0)
701 else
702 marg = SPRITE_HEALTHBAR_MARGIN * t + 0.5 * sz.y;
703
704 float minwidth = (SPRITE_HEALTHBAR_WIDTH + 2 * SPRITE_HEALTHBAR_BORDER) * t;
705 o = drawsprite_TextOrIcon(is_text, o, ang, minwidth, col, a, sz, txt);
707 o,
708 0,
709 GetResource(this, RES_HEALTH),
710 '0 0 0',
711 '0 0 0',
714 marg,
716 align,
717 rgb,
719 rgb,
722 );
723 }
724 else
725 drawsprite_TextOrIcon(is_text, o, ang, 0, col, a, sz, txt);
726
728}
729
731{
732 int dh = search_begin(strcat("models/sprites/*_frame*", ext), false, false);
733 if (dh < 0) return;
734 int ext_len = strlen(ext);
735 int n = search_getsize(dh);
736 for (int i = 0; i < n; ++i)
737 {
738 string s = search_getfilename(dh, i);
739 s = substring(s, 15, strlen(s) - 15 - ext_len); // strip models/sprites/ and extension
740
741 int o = strstrofs(s, "_frame", 0);
742 string sname = strcat("/spriteframes/", substring(s, 0, o));
743 string sframes = substring(s, o + 6, strlen(s) - o - 6);
744 int f = stof(sframes) + 1;
745 db_put(tempdb, sname, ftos(max(f, stof(db_get(tempdb, sname)))));
746 }
747 search_end(dh);
748}
749
757{
780
783}
784#endif
785
786#ifdef SVQC
788{
789 string m1 = _m1.netname;
790 string m2 = _m2.netname;
791 string m3 = _m3.netname;
792 if (m1 != e.model1)
793 {
794 e.model1 = m1;
795 e.SendFlags |= 2;
796 }
797 if (m2 != e.model2)
798 {
799 e.model2 = m2;
800 e.SendFlags |= 4;
801 }
802 if (m3 != e.model3)
803 {
804 e.model3 = m3;
805 e.SendFlags |= 8;
806 }
807}
808
810{
811 f = bound(0, f, e.max_health);
812 float step = e.max_health / 40;
813 if ((floor(f / step) != floor(GetResource(e, RES_HEALTH) / step)) || e.pain_finished)
814 {
815 SetResourceExplicit(e, RES_HEALTH, f);
816 e.pain_finished = 0;
817 e.SendFlags |= 0x80;
818 }
819}
820
822{
823 if (f != e.max_health || e.pain_finished)
824 {
825 e.max_health = f;
826 e.pain_finished = 0;
827 e.SendFlags |= 0x80;
828 }
829}
830
832{
833 if (f != e.pain_finished || e.max_health)
834 {
835 e.max_health = 0;
836 e.pain_finished = f;
837 e.SendFlags |= 0x80;
838 }
839}
840
842{
843 if (o != e.origin)
844 {
845 setorigin(e, o);
846 e.SendFlags |= 64;
847 }
848}
849
850void WaypointSprite_UpdateRule(entity e, float t, float r)
851{
852 // no check, as this is never called without doing an actual change (usually only once)
853 e.rule = r;
854 e.team = t;
855 e.SendFlags |= 1;
856}
857
859{
860 // no check, as this is never called without doing an actual change (usually only once)
861 int i = icon.m_id;
862 int new_cnt = (e.cnt & BIT(7)) | (i & BITS(7));
863 if (new_cnt != e.cnt || col != e.colormod)
864 {
865 e.cnt = new_cnt;
866 e.colormod = col;
867 e.SendFlags |= 32;
868 }
869}
870
872{
873 // anti spam
874 if (time < e.waypointsprite_pingtime) return;
875 e.waypointsprite_pingtime = time + 0.3;
876 // ALWAYS sends (this causes a radar circle), thus no check
877 e.cnt |= BIT(7);
878 e.SendFlags |= 32;
879}
880
882{
884 e.waypointsprite_helpmetime = time + waypointsprite_deployed_lifetime;
885 e.SendFlags |= 32;
886}
887
889{
890 if (!e.fade_time)
891 {
892 e.fade_time = t;
893 e.teleport_time = time + t;
894 }
895 else if (t < (e.teleport_time - time))
896 {
897 // accelerate the waypoint's dying
898 // ensure:
899 // (e.teleport_time - time) / wp.fade_time stays
900 // e.teleport_time = time + fadetime
901 float current_fadetime = e.teleport_time - time;
902 e.teleport_time = time + t;
903 if (e.fade_time < 0)
904 e.fade_time = -e.fade_time;
905 e.fade_time *= t / current_fadetime;
906 }
907
908 e.SendFlags |= 16;
909}
910
917
919{
920 if (!wp) return;
921 if (wp.owner) wp.owner.(wp.owned_by_field) = NULL;
922 delete(wp);
923}
924
926{
927 if (!wp) return;
928 if (wp.classname != "sprite_waypoint")
929 {
930 backtrace("Trying to disown a non-waypointsprite");
931 return;
932 }
933 if (wp.owner)
934 {
935 if (wp.exteriormodeltoclient == wp.owner)
936 wp.exteriormodeltoclient = NULL;
937 wp.owner.(wp.owned_by_field) = NULL;
938 wp.owner = NULL;
939
941 }
942}
943
945{
946 bool doremove = false;
947
948 if (this.fade_time && time >= this.teleport_time)
949 doremove = true;
950
951 if (this.exteriormodeltoclient)
952 WaypointSprite_UpdateOrigin(this, this.exteriormodeltoclient.origin + this.view_ofs);
953
954 if (doremove)
956 else
957 this.nextthink = time; // WHY?!?
958}
959
961{
962 // personal waypoints
963 if (this.enemy && this.enemy != view)
964 return false;
965
966 // team waypoints
967 if (this.rule == SPRITERULE_SPECTATOR)
968 {
970 return false;
971 if (!warmup_stage && IS_PLAYER(view) && autocvar_sv_itemstime != 2)
972 return false;
973 }
974 else if (this.team && this.rule == SPRITERULE_DEFAULT)
975 {
976 if (this.team != view.team)
977 return false;
978 if (!IS_PLAYER(view))
979 return false;
980 }
981
982 return true;
983}
984
986{
987 if (IS_SPEC(e)) e = e.enemy;
988 /* TODO idea (check this breaks nothing)
989 else if (e.classname == "observer")
990 e = NULL;
991 */
992 return e;
993}
994
996{
997 if (teamplay)
998 return e2.team == e.team;
999 return e2 == e;
1000}
1001
1003{
1004 // this is not in SendEntity because it shall run every frame, not just every update
1005
1006 // make spectators see what the player would see
1008
1009 if (MUTATOR_CALLHOOK(CustomizeWaypoint, this, client))
1010 return false;
1011
1012 return this.waypointsprite_visible_for_player(this, client, e);
1013}
1014
1015bool WaypointSprite_SendEntity(entity this, entity to, float sendflags);
1016
1018{
1019 // if a WP wants to time out, let it time out immediately; other WPs ought to be reset/killed by their owners
1020
1021 if (this.fade_time)
1022 WaypointSprite_Kill(this);
1023}
1024
1026 entity spr, // sprite
1027 float _lifetime, float maxdistance, // lifetime, max distance
1028 entity ref, vector ofs, // position
1029 entity showto, float t, // show to whom? Use a flag to indicate a team
1030 entity own, .entity ownfield, // remove when own gets killed
1031 float hideable, // true when it should be controlled by cl_hidewaypoints
1032 entity icon // initial icon
1033)
1034{
1035 entity wp = new(sprite_waypoint);
1036 wp.fade_time = _lifetime; // if negative tells client not to fade it out
1037 if(_lifetime < 0)
1038 _lifetime = -_lifetime;
1039 wp.teleport_time = time + _lifetime;
1040 wp.exteriormodeltoclient = ref;
1041 if (ref)
1042 {
1043 wp.view_ofs = ofs;
1044 setorigin(wp, ref.origin + ofs);
1045 }
1046 else
1047 setorigin(wp, ofs);
1048 wp.enemy = showto;
1049 wp.team = t;
1050 wp.owner = own;
1051 wp.currentammo = hideable;
1052 if (own)
1053 {
1054 if (own.(ownfield))
1055 delete(own.(ownfield));
1056 own.(ownfield) = wp;
1057 wp.owned_by_field = ownfield;
1058 }
1059 wp.fade_rate = maxdistance;
1061 wp.nextthink = time;
1062 wp.model1 = spr.netname;
1064 wp.waypointsprite_visible_for_player = WaypointSprite_visible_for_player;
1065 wp.reset2 = WaypointSprite_Reset;
1066 wp.cnt = icon.m_id;
1067 wp.colormod = spr.m_color;
1069 return wp;
1070}
1071
1073 entity spr,
1074 vector ofs,
1075 entity own,
1076 .entity ownfield,
1077 entity icon // initial icon
1078)
1079{
1080 return WaypointSprite_Spawn(spr, 0, 0, NULL, ofs, NULL, 0, own, ownfield, true, icon);
1081}
1082
1084 entity spr,
1085 bool limited_range,
1086 entity player,
1087 vector ofs,
1088 entity icon // initial icon
1089)
1090{
1091 float t = teamplay ? player.team : 0;
1092 float maxdistance = limited_range ? waypointsprite_limitedrange : 0;
1093 return WaypointSprite_Spawn(spr, waypointsprite_deployed_lifetime, maxdistance, NULL, ofs, NULL, t, player, waypointsprite_deployed_fixed, false, icon);
1094}
1095
1097 entity spr,
1098 entity player,
1099 vector ofs,
1100 entity icon // initial icon
1101)
1102{
1103 return WaypointSprite_Spawn(spr, 0, 0, NULL, ofs, NULL, 0, player, waypointsprite_deployed_personal, false, icon);
1104}
1105
1107 entity spr,
1108 entity player,
1109 bool limited_range,
1110 entity icon // initial icon
1111)
1112{
1113 if (player.waypointsprite_attachedforcarrier)
1114 return NULL; // can't attach to FC
1115 float t = teamplay ? player.team : 0;
1116 float maxdistance = limited_range ? waypointsprite_limitedrange : 0;
1117 return WaypointSprite_Spawn(spr, waypointsprite_deployed_lifetime, maxdistance, player, '0 0 64', NULL, t, player, waypointsprite_attached, false, icon);
1118}
1119
1121 entity spr,
1122 entity carrier,
1123 entity icon // initial icon and color
1124)
1125{
1126 WaypointSprite_Kill(carrier.waypointsprite_attached); // FC overrides attached
1127 entity e = WaypointSprite_Spawn(spr, 0, 0, carrier, '0 0 64', NULL, carrier.team, carrier, waypointsprite_attachedforcarrier, false, icon);
1128 if (GetResource(carrier, RES_HEALTH))
1129 {
1132 }
1133 return e;
1134}
1135
1137{
1138 WaypointSprite_Disown(carrier.waypointsprite_attachedforcarrier, waypointsprite_deadlifetime);
1139}
1140
1145
1152
1158
1166#endif
#define MUTATOR_CALLHOOK(id,...)
Definition base.qh:143
#define REGISTER_MUTATOR(...)
Definition base.qh:295
#define BIT(n)
Only ever assign into the first 24 bits in QC (so max is BIT(23)).
Definition bits.qh:8
#define BITS(n)
Definition bits.qh:9
var entity(vector mins, vector maxs,.entity tofield) findbox_tofield_OrFallback
float max_health
float pain_finished
#define ReadString
float GetResource(entity e, Resource res_type)
Returns the current amount of resource the given entity has.
#define drawstring(position, text, scale, rgb, alpha, flag)
Definition draw.qh:27
#define draw_beginBoldFont()
Definition draw.qh:4
#define drawpic_aspect(pos, pic, mySize, color, theAlpha, drawflag)
Definition draw.qh:57
#define draw_endBoldFont()
Definition draw.qh:5
string netname
Definition powerups.qc:20
float lifetime
Definition powerups.qc:23
float cnt
Definition powerups.qc:24
vector colormod
Definition powerups.qc:21
float alpha
Definition items.qc:13
float spawntime
Definition items.qh:16
vector view_origin
Definition main.qh:109
entity owner
Definition main.qh:87
bool warmup_stage
Definition main.qh:120
int tempdb
Definition main.qh:203
vector mi_scale
Definition main.qh:38
IntrusiveList g_radaricons
Definition main.qh:95
int team
Definition main.qh:188
IntrusiveList g_drawables_2d
Definition main.qh:92
ERASEABLE vector rgb_to_hsv(vector rgb)
Definition color.qh:119
ERASEABLE vector hsv_to_rgb(vector hsv)
Definition color.qh:136
int spawnflags
Definition ammo.qh:15
#define M_ARGV(x, type)
Definition events.qh:17
float teleport_time
Definition player.qh:218
#define IS_PLAYER(s)
Definition player.qh:242
vector healtharmor_maxdamage(float h, float a, float armorblock, int deathtype)
Definition util.qc:1391
const float DRAWFLAG_NORMAL
const float DRAWFLAG_ADDITIVE
float player_localnum
float time
vector size
float nextthink
vector origin
#define stringwidth
float autocvar_g_balance_armor_blockpercent
Definition damage.qh:21
entity exteriormodeltoclient
#define strstrofs
#define strlen
RES_ARMOR
Definition ent_cs.qc:155
WriteString(chan, ent.netname)
WriteByte(chan, ent.angles.y/DEC_FACTOR)
SetResourceExplicit(ent, RES_ARMOR, ReadByte() *DEC_FACTOR)) ENTCS_PROP(NAME
int entcs_GetTeam(int i)
Definition ent_cs.qh:132
Weapons
Definition guide.qh:113
float autocvar__menu_alpha
Definition hud.qh:187
string hud_skin_path
Definition hud.qh:136
float autocvar_hud_panel_fg_alpha
Definition hud.qh:202
void InterpolateOrigin_Undo(entity this)
snap origin to iorigin2 (actual origin)
void InterpolateOrigin_Note(entity this)
void InterpolateOrigin_Do(entity this)
set origin based on iorigin1 (old pos), iorigin2 (desired pos), and time
const int IFLAG_ORIGIN
int iflags
ERASEABLE entity IL_PUSH(IntrusiveList this, entity it)
Push to tail.
int autocvar_sv_itemstime
Definition itemstime.qh:4
#define FOREACH(list, cond, body)
Definition iter.qh:19
noref float vid_conwidth
Definition draw.qh:7
noref float vid_conheight
Definition draw.qh:8
#define NET_HANDLE(id, param)
Definition net.qh:15
float servertime
Definition net.qh:329
const int MSG_ENTITY
Definition net.qh:156
#define ReadVector()
Definition net.qh:349
#define WriteHeader(to, id)
Definition net.qh:265
#define REGISTER_NET_LINKED(id)
Definition net.qh:91
void Net_LinkEntity(entity e, bool docull, float dt, bool(entity this, entity to, int sendflags) sendfunc)
Definition net.qh:167
int ReadByte()
#define STAT(...)
Definition stats.qh:94
#define backtrace(msg)
Definition log.qh:96
#define LOG_INFOF(...)
Definition log.qh:63
ERASEABLE string db_get(int db, string key)
Definition map.qh:93
ERASEABLE void db_put(int db, string key, string value)
Definition map.qh:103
const float M_SQRT2
sqrt(2)
Definition mathlib.qh:120
#define M_PI
pi
Definition mathlib.qh:114
float stof(string val,...)
float bound(float min, float value, float max)
string substring(string s, float start, float length)
string search_getfilename(float handle, float num)
float cos(float f)
string precache_pic(string name,...)
float search_getsize(float handle)
float vlen(vector v)
void WriteShort(float data, float dest, float desto)
float sqrt(float f)
float search_begin(string pattern, float caseinsensitive, float quiet)
void WriteCoord(float data, float dest, float desto)
float sin(float f)
float min(float f,...)
string ftos(float f)
float fabs(float f)
float floor(float f)
void search_end(float handle)
float max(float f,...)
fadetime
#define get_monsterinfo(i)
Definition all.qh:8
string string_null
Definition nil.qh:9
spree_inf s1 s2 s3loc s2 spree_inf s1 s2 s3loc s2 spree_inf s1 s2 s3loc s2 s1 s2loc s1 s2loc s1 s2loc s1 s2loc s1 s2loc s1 s2loc s1 s2 f1points f2
Definition all.inc:370
f1
Definition all.inc:569
strcat(_("^F4Countdown stopped!"), "\n^BG", _("Teams are too unbalanced."))
#define NULL
Definition post.qh:14
#define error
Definition pre.qh:6
float currentammo
Definition progsdefs.qc:142
fade_rate
Definition projectile.qh:14
float race_checkpointtime
Definition racetimer.qh:13
float race_mycheckpointtime
Definition racetimer.qh:37
#define REGISTRY_GET(id, i)
Definition registry.qh:62
#define setthink(e, f)
vector
Definition self.qh:96
#define setcefc(e, f)
vector org
Definition self.qh:96
vector vector ang
Definition self.qh:96
float fade_time
Definition common.qh:35
#define STATIC_INIT(func)
during worldspawn
Definition static.qh:33
#define strfree(this)
Definition string.qh:57
#define strcpy(this, s)
Definition string.qh:51
entity enemy
Definition sv_ctf.qh:152
const int MAX_TEAMRADAR_TIMES
Definition teamradar.qh:3
const int NUM_SPECTATOR
Definition teams.qh:23
bool teamplay
Definition teams.qh:59
#define IS_SPEC(v)
Definition utils.qh:10
const vector eY
Definition vector.qh:44
ERASEABLE vector Rotate(vector v, float a)
Definition vector.qh:111
const vector eX
Definition vector.qh:43
vector project_3d_to_2d(vector vec)
Definition view.qc:373
vector drawsprite_TextOrIcon(bool is_text, vector o, float ang, float minwidth, vector rgb, float a, vector sz, string str)
void WaypointSprite_HelpMePing(entity e)
void WaypointSprite_ClearOwned(entity this)
entity WaypointSprite_DeployPersonal(entity spr, entity player, vector ofs, entity icon)
bool WaypointSprite_Customize(entity this, entity client)
bool WaypointSprite_SendEntity(entity this, entity to, float sendflags)
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)
void WaypointSprite_Kill(entity wp)
void WaypointSprite_UpdateTeamRadar(entity e, entity icon, vector col)
entity WaypointSprite_DeployFixed(entity spr, bool limited_range, entity player, vector ofs, entity icon)
void drawquad(vector o, vector ri, vector up, string pic, vector rgb, float a, float f)
string spritelookupicon(entity this, string s)
void WaypointSprite_UpdateOrigin(entity e, vector o)
void WaypointSprite_Init()
void WaypointSprite_UpdateSprites(entity e, entity _m1, entity _m2, entity _m3)
void WaypointSprite_UpdateMaxHealth(entity e, float f)
void WaypointSprite_Ping(entity e)
void WaypointSprite_ClearPersonal(entity this)
vector drawspritearrow(vector o, float ang, vector rgb, float a, float t)
entity WaypointSprite_Spawn(entity spr, float _lifetime, float maxdistance, entity ref, vector ofs, entity showto, float t, entity own,.entity ownfield, float hideable, entity icon)
void WaypointSprite_Load()
string spritelookuptext(entity this, string s)
void WaypointSprite_Think(entity this)
bool WaypointSprite_visible_for_player(entity this, entity player, entity view)
entity WaypointSprite_getviewentity(entity e)
entity WaypointSprite_Attach(entity spr, entity player, bool limited_range, entity icon)
vector fixrgbexcess_move(vector rgb, vector src, vector dst)
void Ent_RemoveWaypointSprite(entity this)
vector spritelookupcolor(entity this, string s, vector def)
void WaypointSprite_FadeOutIn(entity e, float t)
entity WaypointSprite_AttachCarrier(entity spr, entity carrier, entity icon)
void WaypointSprite_PlayerGone(entity this)
void Draw_WaypointSprite(entity this)
void WaypointSprite_PlayerDead(entity this)
void Ent_WaypointSprite(entity this, bool isnew)
void WaypointSprite_UpdateHealth(entity e, float f)
vector fixrgbexcess(vector rgb)
float WaypointSprite_isteammate(entity e, entity e2)
entity WaypointSprite_SpawnFixed(entity spr, vector ofs, entity own,.entity ownfield, entity icon)
void WaypointSprite_Disown(entity wp, float fadetime)
void WaypointSprite_DetachCarrier(entity carrier)
void WaypointSprite_UpdateBuildFinished(entity e, float f)
void WaypointSprite_Reset(entity this)
void drawrotpic(vector org, float rot, string pic, vector sz, vector hotspot, vector rgb, float a, float f)
void WaypointSprite_Load_Frames(string ext)
float spritelookupblinkvalue(entity this, string s)
void WaypointSprite_UpdateRule(entity e, float t, float r)
const float SPRITE_HEALTHBAR_HEIGHT
entity waypointsprite_deployed_fixed
float autocvar_g_waypointsprite_edgeoffset_left
float autocvar_g_waypointsprite_timealphaexponent
float waypointsprite_limitedrange
float waypointsprite_count
float waypointsprite_alpha
const int SPRITERULE_DEFAULT
int wp_extra
Additional networked waypoint state, used for items, weapons, buffs.
bool autocvar_g_waypointsprite_uppercase
float waypointsprite_edgeoffset_left
float autocvar_g_waypointsprite_crosshairfadedistance
float waypointsprite_helpmetime
float autocvar_g_waypointsprite_fontsize
float waypointsprite_scale
float autocvar_g_waypointsprite_normdistance
float waypointsprite_deadlifetime
float waypointsprite_distancefadedistance
float waypointsprite_edgeoffset_top
entity waypointsprite_attached
float autocvar_g_waypointsprite_distancefadealpha
float autocvar_g_waypointsprite_iconsize
float autocvar_g_waypointsprite_alpha
float waypointsprite_distancealphaexponent
float autocvar_g_waypointsprite_crosshairfadealpha
float waypointsprite_crosshairfadescale
const float SPRITE_HELPME_BLINK
float waypointsprite_normdistance
float waypointsprite_edgefadescale
int autocvar_g_waypointsprite_itemstime
float waypointsprite_edgeoffset_right
float waypointsprite_crosshairfadealpha
float waypointsprite_fadedistance
float waypointsprite_edgefadedistance
entity waypointsprite_deployed_personal
float autocvar_g_waypointsprite_minalpha
const float SPRITE_ARROW_SCALE
float autocvar_sv_waypointsprite_limitedrange
float autocvar_sv_waypointsprite_deadlifetime
float waypointsprite_crosshairfadedistance
string model1
bool autocvar_g_waypointsprite_iconcolor
float autocvar_g_waypointsprite_scale
float waypointsprite_deployed_lifetime
bool autocvar_cl_hidewaypoints
entity waypointsprite_attachedforcarrier
const int SPRITERULE_SPECTATOR
float autocvar_g_waypointsprite_distancealphaexponent
float autocvar_g_waypointsprite_edgeoffset_right
float autocvar_g_waypointsprite_edgeoffset_top
const float SPRITE_HEALTHBAR_HEALTHALPHA
string model3
float autocvar_g_waypointsprite_distancefadescale
float autocvar_g_waypointsprite_edgefadescale
const float SPRITE_HEALTHBAR_BORDERALPHA
float autocvar_g_waypointsprite_crosshairfadescale
float waypointsprite_minscale
float waypointsprite_edgefadealpha
const float SPRITE_HEALTHBAR_WIDTH
const float SPRITE_HEALTHBAR_BORDER
float autocvar_g_waypointsprite_edgefadedistance
float waypointsprite_distancefadealpha
float autocvar_g_waypointsprite_edgeoffset_bottom
float waypointsprite_timealphaexponent
float rule
float waypointsprite_distancefadescale
float autocvar_g_waypointsprite_minscale
const float SPRITE_HEALTHBAR_MARGIN
float autocvar_g_waypointsprite_distancefadedistancemultiplier
float waypointsprite_fontsize
float autocvar_sv_waypointsprite_deployed_lifetime
const int SPRITERULE_TEAMPLAY
float waypointsprite_minalpha
string model2
float waypointsprite_edgeoffset_bottom
float waypointsprite_newcount
int autocvar_g_waypointsprite_spam
bool autocvar_g_waypointsprite_text
float autocvar_g_waypointsprite_edgefadealpha
const int WEP_FLAG_SUPERWEAPON
powerup timer
Definition weapon.qh:255
float start_armorvalue
Definition world.qh:96
float start_health
Definition world.qh:95