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 {
34 WriteByte(MSG_ENTITY, (GetResource(this, RES_HEALTH) / this.max_health) * 191.0);
35 }
36 else
37 {
38 float dt = this.pain_finished - time;
39 dt = bound(0, dt * 32, 16383);
40 WriteByte(MSG_ENTITY, (dt & 0xFF00) / 256 + 192);
41 WriteByte(MSG_ENTITY, (dt & 0x00FF));
42 }
43 }
44
45 if (sendflags & 64)
46 {
47 WriteVector(MSG_ENTITY, this.origin);
48 }
49
50 if (sendflags & 1)
51 {
54 }
55
56 if (sendflags & 2)
58
59 if (sendflags & 4)
61
62 if (sendflags & 8)
64
65 if (sendflags & 16)
66 {
69 WriteShort(MSG_ENTITY, bound(0, this.fade_rate, 32767)); // maxdist
70 WriteByte(MSG_ENTITY, hide_flags);
71 }
72
73 if (sendflags & 32)
74 {
75 WriteByte(MSG_ENTITY, this.cnt); // icon on radar
76 WriteByte(MSG_ENTITY, this.colormod.x * 255.0);
77 WriteByte(MSG_ENTITY, this.colormod.y * 255.0);
78 WriteByte(MSG_ENTITY, this.colormod.z * 255.0);
79
81 {
82 float dt = bound(0, (this.waypointsprite_helpmetime - time) / 0.1, 255);
84 }
85 else
87 }
88
89 return true;
90}
91#endif
92
93#ifdef CSQC
94void Ent_WaypointSprite(entity this, bool isnew);
95NET_HANDLE(waypointsprites, bool isnew) {
96 Ent_WaypointSprite(this, isnew);
97 return true;
98}
99
101{
102 strfree(this.netname);
103 strfree(this.netname2);
104 strfree(this.netname3);
105}
106
107void Ent_WaypointSprite(entity this, bool isnew)
108{
109 int sendflags = ReadByte();
110 this.wp_extra = ReadByte();
111
112 if (!this.spawntime)
113 this.spawntime = time;
114
115 this.draw2d = Draw_WaypointSprite;
116 if (isnew) {
117 IL_PUSH(g_drawables_2d, this);
118 IL_PUSH(g_radaricons, this);
119 }
120
122 this.iflags |= IFLAG_ORIGIN;
123
124 if (sendflags & 0x80)
125 {
126 int t = ReadByte();
127 if (t < 192)
128 {
129 SetResourceExplicit(this, RES_HEALTH, t / 191.0);
130 this.build_finished = 0;
131 }
132 else
133 {
134 t = (t - 192) * 256 + ReadByte();
135 this.build_started = servertime;
136 if (this.build_finished)
137 this.build_starthealth = bound(0, GetResource(this, RES_HEALTH), 1);
138 else
139 this.build_starthealth = 0;
140 this.build_finished = servertime + t / 32;
141 }
142 }
143 else
144 {
145 SetResourceExplicit(this, RES_HEALTH, -1);
146 this.build_finished = 0;
147 }
148
149 if (sendflags & 64)
150 {
151 // unfortunately, this needs to be exact (for the 3D display)
152 this.origin = ReadVector();
153 setorigin(this, this.origin);
154 }
155
156 if (sendflags & 1)
157 {
158 this.team = ReadByte();
159 this.rule = ReadByte();
160 }
161
162 if (sendflags & 2)
163 {
164 strcpy(this.netname, ReadString());
165 }
166
167 if (sendflags & 4)
168 {
169 strcpy(this.netname2, ReadString());
170 }
171
172 if (sendflags & 8)
173 {
174 strcpy(this.netname3, ReadString());
175 }
176
177 if (sendflags & 16)
178 {
179 this.lifetime = ReadCoord();
180 this.fadetime = ReadCoord();
181 this.maxdistance = ReadShort();
182 this.hideflags = ReadByte();
183 }
184
185 if (sendflags & 32)
186 {
187 int f = ReadByte();
188 this.teamradar_icon = f & BITS(7);
189 if (f & BIT(7))
190 {
191 this.(teamradar_times[this.teamradar_time_index]) = time;
192 this.teamradar_time_index = (this.teamradar_time_index + 1) % MAX_TEAMRADAR_TIMES;
193 }
194 this.teamradar_color_x = ReadByte() / 255.0;
195 this.teamradar_color_y = ReadByte() / 255.0;
196 this.teamradar_color_z = ReadByte() / 255.0;
197 this.helpme = ReadByte() * 0.1;
198 if (this.helpme > 0)
199 this.helpme += servertime;
200 }
201
203
204 this.entremove = Ent_RemoveWaypointSprite;
205}
206#endif
207
208#ifdef CSQC
209float spritelookupblinkvalue(entity this, string s)
210{
211 if (s == WP_Weapon.netname) {
213 return 2;
214 }
215 if (s == WP_Item.netname) return REGISTRY_GET(Items, this.wp_extra).m_waypointblink;
216 if(s == WP_FlagReturn.netname) return 2;
217
218 return 1;
219}
220
222{
223 if (s == WP_Weapon.netname || s == RADARICON_Weapon.netname) return REGISTRY_GET(Weapons, this.wp_extra).m_color;
224 if (s == WP_Item.netname || s == RADARICON_Item.netname) return REGISTRY_GET(Items, this.wp_extra).m_color;
225 if (MUTATOR_CALLHOOK(WP_Format, this, s))
226 {
227 return M_ARGV(2, vector);
228 }
229 return def;
230}
231
232string spritelookuptext(entity this, string s)
233{
235 return "Spam"; // no need to translate this debug string
236 if (s == WP_RaceStartFinish.netname) return (race_checkpointtime || race_mycheckpointtime) ? _("Finish") : _("Start");
237 if (s == WP_Weapon.netname) return REGISTRY_GET(Weapons, this.wp_extra).m_name;
238 if (s == WP_Item.netname) return REGISTRY_GET(Items, this.wp_extra).m_waypoint;
239 if (s == WP_Monster.netname) return get_monsterinfo(this.wp_extra).m_name;
240 if (MUTATOR_CALLHOOK(WP_Format, this, s))
241 {
242 return M_ARGV(3, string);
243 }
244
245 // need to loop, as our netname could be one of three
246 FOREACH(Waypoints, it.netname == s, {
247 return it.m_name;
248 });
249
250 return s;
251}
252
253string spritelookupicon(entity this, string s)
254{
255 // TODO: needs icons! //if (s == WP_RaceStartFinish.netname) return (race_checkpointtime || race_mycheckpointtime) ? _("Finish") : _("Start");
256 if (s == WP_Weapon.netname) return REGISTRY_GET(Weapons, this.wp_extra).model2;
257 if (s == WP_Item.netname) return REGISTRY_GET(Items, this.wp_extra).m_icon;
258 if (s == WP_Vehicle.netname) return REGISTRY_GET(Vehicles, this.wp_extra).m_icon;
259 //if (s == WP_Monster.netname) return get_monsterinfo(this.wp_extra).m_icon;
260 if (MUTATOR_CALLHOOK(WP_Format, this, s))
261 {
262 return M_ARGV(4, string);
263 }
264
265 // need to loop, as our netname could be one of three
266 FOREACH(Waypoints, it.netname == s, {
267 return it.m_icon;
268 });
269
270 return s;
271}
272#endif
273
274#ifdef CSQC
275void drawrotpic(vector org, float rot, string pic, vector sz, vector hotspot, vector rgb, float a, float f)
276{
277 vector v1, v2, v3, v4;
278
279 hotspot = -1 * hotspot;
280
281 // hotspot-relative coordinates of the corners
282 v1 = hotspot;
283 v2 = hotspot + '1 0 0' * sz.x;
284 v3 = hotspot + '1 0 0' * sz.x + '0 1 0' * sz.y;
285 v4 = hotspot + '0 1 0' * sz.y;
286
287 // rotate them, and make them absolute
288 rot = -rot; // rotate by the opposite angle, as our coordinate system is reversed
289 v1 = Rotate(v1, rot) + org;
290 v2 = Rotate(v2, rot) + org;
291 v3 = Rotate(v3, rot) + org;
292 v4 = Rotate(v4, rot) + org;
293
294 // draw them
295 R_BeginPolygon(pic, f, true);
296 R_PolygonVertex(v1, '0 0 0', rgb, a);
297 R_PolygonVertex(v2, '1 0 0', rgb, a);
298 R_PolygonVertex(v3, '1 1 0', rgb, a);
299 R_PolygonVertex(v4, '0 1 0', rgb, a);
300 R_EndPolygon();
301}
302
303void drawquad(vector o, vector ri, vector up, string pic, vector rgb, float a, float f)
304{
305 R_BeginPolygon(pic, f, true);
306 R_PolygonVertex(o, '0 0 0', rgb, a);
307 R_PolygonVertex(o + ri, '1 0 0', rgb, a);
308 R_PolygonVertex(o + up + ri, '1 1 0', rgb, a);
309 R_PolygonVertex(o + up, '0 1 0', rgb, a);
310 R_EndPolygon();
311}
312
313void 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)
314{
315 vector o, ri, up;
316 float owidth; // outer width
317
318 hotspot = -1 * hotspot;
319
320 // hotspot-relative coordinates of the healthbar corners
321 o = hotspot;
322 ri = '1 0 0';
323 up = '0 1 0';
324
325 rot = -rot; // rotate by the opposite angle, as our coordinate system is reversed
326 o = Rotate(o, rot) + org;
327 ri = Rotate(ri, rot);
328 up = Rotate(up, rot);
329
330 owidth = width + 2 * border;
331 o += up * -(margin + border + theheight) + ri * (sz.x - owidth) * 0.5;
332
333 drawquad(o - up * border, ri * owidth, up * border, "", rgb, a, f);
334 drawquad(o + up * theheight, ri * owidth, up * border, "", rgb, a, f);
335 drawquad(o, ri * border, up * theheight, "", rgb, a, f);
336 drawquad(o + ri * (owidth - border), ri * border, up * theheight, "", rgb, a, f);
337 drawquad(o + ri * (border + align * ((1 - h) * width)), ri * width * h, up * theheight, "", hrgb, ha, f);
338}
339
340// returns location of sprite text
341vector drawspritearrow(vector o, float ang, vector rgb, float a, float t)
342{
343 float size = 9.0 * t;
344 float border = 1.5 * t;
345 float margin = 4.0 * t;
346
347 float borderDiag = border * M_SQRT2;
348 vector arrowX = eX * size;
349 vector arrowY = eY * (size+borderDiag);
350 vector borderX = eX * (size+borderDiag);
351 vector borderY = eY * (size+borderDiag+border);
352
353 R_BeginPolygon("", DRAWFLAG_NORMAL, true);
354 R_PolygonVertex(o, '0 0 0', '0 0 0', a);
355 R_PolygonVertex(o + Rotate(arrowY - borderX, ang), '0 0 0', '0 0 0', a);
356 R_PolygonVertex(o + Rotate(borderY - borderX, ang), '0 0 0', '0 0 0', a);
357 R_PolygonVertex(o + Rotate(borderY + borderX, ang), '0 0 0', '0 0 0', a);
358 R_PolygonVertex(o + Rotate(arrowY + borderX, ang), '0 0 0', '0 0 0', a);
359 R_EndPolygon();
360
361 R_BeginPolygon("", DRAWFLAG_ADDITIVE, true);
362 R_PolygonVertex(o + Rotate(eY * borderDiag, ang), '0 0 0', rgb, a);
363 R_PolygonVertex(o + Rotate(arrowY - arrowX, ang), '0 0 0', rgb, a);
364 R_PolygonVertex(o + Rotate(arrowY + arrowX, ang), '0 0 0', rgb, a);
365 R_EndPolygon();
366
367 return o + Rotate(eY * (borderDiag+size+margin), ang);
368}
369
370// returns location of sprite healthbar
371vector drawsprite_TextOrIcon(bool is_text, vector o, float ang, float minwidth, vector rgb, float a, vector sz, string str)
372{
373 float algnx, algny;
374 float sw, w, h;
375 float aspect, sa, ca;
376
377 if (is_text)
378 sw = stringwidth(str, false, sz);
379 else
380 sw = sz.x;
381
382 if (sw > minwidth)
383 w = sw;
384 else
385 w = minwidth;
386 h = sz.y;
387
388 // how do corners work?
389 aspect = vid_conwidth / vid_conheight;
390 sa = sin(ang);
391 ca = cos(ang) * aspect;
392 if (fabs(sa) > fabs(ca))
393 {
394 algnx = (sa < 0);
395 float f = fabs(sa);
396 algny = 0.5 - 0.5 * (f ? (ca / f) : 0);
397 }
398 else
399 {
400 float f = fabs(ca);
401 algnx = 0.5 - 0.5 * (f ? (sa / f) : 0);
402 algny = (ca < 0);
403 }
404
405 // align
406 o.x -= w * algnx;
407 o.y -= h * algny;
408
409 // we want to be onscreen
410 if (o.x < 0)
411 o.x = 0;
412 if (o.y < 0)
413 o.y = 0;
414 if (o.x > vid_conwidth - w)
415 o.x = vid_conwidth - w;
416 if (o.y > vid_conheight - h)
417 o.y = vid_conheight - h;
418
419 o.x += 0.5 * (w - sw);
420
421 if (is_text)
422 drawstring(o, str, sz, rgb, a, DRAWFLAG_NORMAL);
423 else
424 drawpic(o, str, sz, rgb, a, DRAWFLAG_NORMAL);
425
426 o.x += 0.5 * sw;
427 o.y += 0.5 * h;
428
429 return o;
430}
431
433{
434 vector yvec = '0.299 0.587 0.114';
435 return rgb + dst * ((src * yvec) / (dst * yvec)) * ((rgb - '1 1 1') * src);
436}
437
439{
440 if (rgb.x > 1) {
441 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 1 1');
442 if (rgb.y > 1) {
443 rgb = fixrgbexcess_move(rgb, '0 1 0', '0 0 1');
444 if (rgb.z > 1) rgb.z = 1;
445 } else if (rgb.z > 1) {
446 rgb = fixrgbexcess_move(rgb, '0 0 1', '0 1 0');
447 if (rgb.y > 1) rgb.y = 1;
448 }
449 } else if (rgb.y > 1) {
450 rgb = fixrgbexcess_move(rgb, '0 1 0', '1 0 1');
451 if (rgb.x > 1) {
452 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 0 1');
453 if (rgb.z > 1) rgb.z = 1;
454 } else if (rgb.z > 1) {
455 rgb = fixrgbexcess_move(rgb, '0 0 1', '1 0 0');
456 if (rgb.x > 1) rgb.x = 1;
457 }
458 } else if (rgb.z > 1) {
459 rgb = fixrgbexcess_move(rgb, '0 0 1', '1 1 0');
460 if (rgb.x > 1) {
461 rgb = fixrgbexcess_move(rgb, '1 0 0', '0 1 0');
462 if (rgb.y > 1) rgb.y = 1;
463 } else if (rgb.y > 1) {
464 rgb = fixrgbexcess_move(rgb, '0 1 0', '1 0 0');
465 if (rgb.x > 1) rgb.x = 1;
466 }
467 }
468 return rgb;
469}
470
472{
473 if (this.lifetime > 0)
474 this.alpha = (bound(0, (this.fadetime - time) / this.lifetime, 1) ** waypointsprite_timealphaexponent);
475 else
476 this.alpha = 1;
477
478 if (this.hideflags & 2)
479 return; // radar only
480
482 return;
483
484 if ((this.hideflags & 1) && autocvar_cl_hidewaypoints)
485 return; // fixed waypoint
486
488
489 float t = entcs_GetTeam(player_localnum) + 1;
490 string spriteimage = "";
491
492 // choose the sprite
493 switch (this.rule)
494 {
496 if (!(
498 || (autocvar_g_waypointsprite_itemstime == 2 && (t == NUM_SPECTATOR + 1 || warmup_stage || STAT(ITEMSTIME) == 2))
499 ))
500 return;
501 spriteimage = this.netname;
502 break;
504 if (this.team)
505 {
506 if (this.team == t)
507 spriteimage = this.netname;
508 else
509 spriteimage = "";
510 }
511 else
512 spriteimage = this.netname;
513 break;
515 if (t == NUM_SPECTATOR + 1)
516 spriteimage = this.netname3;
517 else if (this.team == t)
518 spriteimage = this.netname2;
519 else
520 spriteimage = this.netname;
521 break;
522 default:
523 error("Invalid waypointsprite rule!");
524 break;
525 }
526
527 if (spriteimage == "")
528 return;
529
531
532 float dist = vlen(this.origin - view_origin);
533 float a = this.alpha * autocvar_hud_panel_fg_alpha;
534
535 if(this.maxdistance > 0)
536 {
537 // restrict maximum normal distance to the waypoint's maximum distance to prevent exploiting cvars
538 float maxnormdistance = bound(0, waypointsprite_normdistance, this.maxdistance - 1);
539 a *= (bound(0, (this.maxdistance - dist) / (this.maxdistance - maxnormdistance), 1) ** waypointsprite_distancealphaexponent);
540 }
541
542 vector rgb = spritelookupcolor(this, spriteimage, this.teamradar_color);
543 if (rgb == '0 0 0')
544 {
545 this.teamradar_color = '1 0 1';
546 LOG_INFOF("WARNING: sprite of name %s has no color, using pink so you notice it", spriteimage);
547 }
548
549 float health_val = GetResource(this, RES_HEALTH);
550 float blink_time = (health_val >= 0) ? (health_val * 10) : time;
551 if (blink_time - floor(blink_time) > 0.5)
552 {
553 if (this.helpme && time < this.helpme)
555 else if (!this.lifetime) // fading out waypoints don't blink
556 a *= spritelookupblinkvalue(this, spriteimage);
557 }
558
559 if (a > 1)
560 {
561 rgb *= a;
562 a = 1;
563 }
564
565 if (a <= 0.003)
566 return;
567
568 rgb = fixrgbexcess(rgb);
569
570 vector o;
571 float ang;
572
573 o = project_3d_to_2d(this.origin);
574 if (o.z < 0
579 {
580 // scale it to be just in view
581 vector d = o - '0.5 0 0' * vid_conwidth - '0 0.5 0' * vid_conheight;
582 ang = atan2(-d.x, -d.y);
583 if (o.z < 0)
584 ang += M_PI;
585
586 float f1 = d.x / vid_conwidth;
587 float f2 = d.y / vid_conheight;
588 if (f1 == 0) { f1 = 0.000001; }
589 if (f2 == 0) { f2 = 0.000001; }
590
591 if (max(f1, -f1) > max(f2, -f2)) {
592 if (d.z * f1 > 0) {
593 // RIGHT edge
594 d *= (0.5 - waypointsprite_edgeoffset_right) / f1;
595 } else {
596 // LEFT edge
597 d *= -(0.5 - waypointsprite_edgeoffset_left) / f1;
598 }
599 } else {
600 if (d.z * f2 > 0) {
601 // BOTTOM edge
603 } else {
604 // TOP edge
605 d *= -(0.5 - waypointsprite_edgeoffset_top) / f2;
606 }
607 }
608
609 o = d + '0.5 0 0' * vid_conwidth + '0 0.5 0' * vid_conheight;
610 }
611 else
612 {
613#if 1
614 ang = M_PI;
615#else
616 vector d = o - '0.5 0 0' * vid_conwidth - '0 0.5 0' * vid_conheight;
617 ang = atan2(-d.x, -d.y);
618#endif
619 }
620 o.z = 0;
621
622 float edgedistance_min = min((o.y - (vid_conheight * waypointsprite_edgeoffset_top)),
626
627 float crosshairdistance = sqrt( ((o.x - vid_conwidth/2) ** 2) + ((o.y - vid_conheight/2) ** 2) );
628
631
632 {
635 }
636 if (edgedistance_min < waypointsprite_edgefadedistance) {
637 a *= 1 - (1 - waypointsprite_edgefadealpha) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1));
638 t *= 1 - (1 - waypointsprite_edgefadescale) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1));
639 }
640 if (crosshairdistance < waypointsprite_crosshairfadedistance) {
641 a *= 1 - (1 - waypointsprite_crosshairfadealpha) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1));
642 t *= 1 - (1 - waypointsprite_crosshairfadescale) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1));
643 }
644
645 if (this.build_finished)
646 {
647 if (time < this.build_finished + 0.25)
648 {
649 if (time < this.build_started)
650 SetResourceExplicit(this, RES_HEALTH, this.build_starthealth);
651 else if (time < this.build_finished)
652 SetResourceExplicit(this, RES_HEALTH, (time - this.build_started) / (this.build_finished - this.build_started) * (1 - this.build_starthealth) + this.build_starthealth);
653 else
654 SetResourceExplicit(this, RES_HEALTH, 1);
655 }
656 else
657 SetResourceExplicit(this, RES_HEALTH, -1);
658 }
659
660 o = drawspritearrow(o, ang, rgb, a, SPRITE_ARROW_SCALE * t);
661
662 string pic = "";
663 bool is_text = true;
665 {
666 string spr_icon = spritelookupicon(this, spriteimage);
667 pic = spr_icon;
668 bool icon_found = !(!spr_icon || spr_icon == "");
669 if (icon_found) // it's valid, but let's make sure it exists!
670 {
671 pic = strcat(hud_skin_path, "/", spr_icon);
672 if(precache_pic(pic) == "")
673 {
674 pic = strcat("gfx/hud/default/", spr_icon);
675 if(!precache_pic(pic))
676 icon_found = false;
677 }
678 }
679 if (icon_found)
680 is_text = false;
681 }
682
683 vector sz;
684 vector col = rgb;
685 string txt = string_null; // it will contain either the text or the icon path
686 if (is_text)
687 {
688 txt = spritelookuptext(this, spriteimage);
689 if (this.helpme && time < this.helpme)
690 txt = sprintf(_("%s needing help!"), txt);
692 txt = strtoupper(txt);
693 sz = waypointsprite_fontsize * '1 1 0';
694 }
695 else
696 {
697 txt = pic; // icon path
699 col = '1 1 1';
701 {
702 col = rgb_to_hsv(col);
703 col.y *= autocvar_g_waypointsprite_iconcolor; // scale saturation
704 col = hsv_to_rgb(col);
705 }
707 }
708
710 if (GetResource(this, RES_HEALTH) >= 0)
711 {
712 float align = 0, marg;
713 if (this.build_finished)
714 align = 0.5;
715 else
716 align = 0;
717 if (cos(ang) > 0)
719 else
720 marg = SPRITE_HEALTHBAR_MARGIN * t + 0.5 * sz.y;
721
722 float minwidth = (SPRITE_HEALTHBAR_WIDTH + 2 * SPRITE_HEALTHBAR_BORDER) * t;
723 o = drawsprite_TextOrIcon(is_text, o, ang, minwidth, col, a, sz, txt);
725 o,
726 0,
727 GetResource(this, RES_HEALTH),
728 '0 0 0',
729 '0 0 0',
732 marg,
734 align,
735 rgb,
737 rgb,
740 );
741 }
742 else
743 {
744 drawsprite_TextOrIcon(is_text, o, ang, 0, col, a, sz, txt);
745 }
746
748}
749
751{
752 int dh = search_begin(strcat("models/sprites/*_frame*", ext), false, false);
753 if (dh < 0) return;
754 int ext_len = strlen(ext);
755 int n = search_getsize(dh);
756 for (int i = 0; i < n; ++i)
757 {
758 string s = search_getfilename(dh, i);
759 s = substring(s, 15, strlen(s) - 15 - ext_len); // strip models/sprites/ and extension
760
761 int o = strstrofs(s, "_frame", 0);
762 string sname = strcat("/spriteframes/", substring(s, 0, o));
763 string sframes = substring(s, o + 6, strlen(s) - o - 6);
764 int f = stof(sframes) + 1;
765 db_put(tempdb, sname, ftos(max(f, stof(db_get(tempdb, sname)))));
766 }
767 search_end(dh);
768}
769
777{
800
803}
804#endif
805
806#ifdef SVQC
808{
809 string m1 = _m1.netname;
810 string m2 = _m2.netname;
811 string m3 = _m3.netname;
812 if (m1 != e.model1)
813 {
814 e.model1 = m1;
815 e.SendFlags |= 2;
816 }
817 if (m2 != e.model2)
818 {
819 e.model2 = m2;
820 e.SendFlags |= 4;
821 }
822 if (m3 != e.model3)
823 {
824 e.model3 = m3;
825 e.SendFlags |= 8;
826 }
827}
828
830{
831 f = bound(0, f, e.max_health);
832 float step = e.max_health / 40;
833 if ((floor(f / step) != floor(GetResource(e, RES_HEALTH) / step)) || e.pain_finished)
834 {
835 SetResourceExplicit(e, RES_HEALTH, f);
836 e.pain_finished = 0;
837 e.SendFlags |= 0x80;
838 }
839}
840
842{
843 if (f != e.max_health || e.pain_finished)
844 {
845 e.max_health = f;
846 e.pain_finished = 0;
847 e.SendFlags |= 0x80;
848 }
849}
850
852{
853 if (f != e.pain_finished || e.max_health)
854 {
855 e.max_health = 0;
856 e.pain_finished = f;
857 e.SendFlags |= 0x80;
858 }
859}
860
862{
863 if (o != e.origin)
864 {
865 setorigin(e, o);
866 e.SendFlags |= 64;
867 }
868}
869
870void WaypointSprite_UpdateRule(entity e, float t, float r)
871{
872 // no check, as this is never called without doing an actual change (usually only once)
873 e.rule = r;
874 e.team = t;
875 e.SendFlags |= 1;
876}
877
879{
880 // no check, as this is never called without doing an actual change (usually only once)
881 int i = icon.m_id;
882 int new_cnt = (e.cnt & BIT(7)) | (i & BITS(7));
883 if (new_cnt != e.cnt || col != e.colormod)
884 {
885 e.cnt = new_cnt;
886 e.colormod = col;
887 e.SendFlags |= 32;
888 }
889}
890
892{
893 // anti spam
894 if (time < e.waypointsprite_pingtime) return;
895 e.waypointsprite_pingtime = time + 0.3;
896 // ALWAYS sends (this causes a radar circle), thus no check
897 e.cnt |= BIT(7);
898 e.SendFlags |= 32;
899}
900
902{
904 e.waypointsprite_helpmetime = time + waypointsprite_deployed_lifetime;
905 e.SendFlags |= 32;
906}
907
909{
910 if (!e.fade_time)
911 {
912 e.fade_time = t;
913 e.teleport_time = time + t;
914 }
915 else if (t < (e.teleport_time - time))
916 {
917 // accelerate the waypoint's dying
918 // ensure:
919 // (e.teleport_time - time) / wp.fade_time stays
920 // e.teleport_time = time + fadetime
921 float current_fadetime = e.teleport_time - time;
922 e.teleport_time = time + t;
923 if (e.fade_time < 0)
924 e.fade_time = -e.fade_time;
925 e.fade_time *= t / current_fadetime;
926 }
927
928 e.SendFlags |= 16;
929}
930
937
939{
940 if (!wp) return;
941 if (wp.owner) wp.owner.(wp.owned_by_field) = NULL;
942 delete(wp);
943}
944
946{
947 if (!wp) return;
948 if (wp.classname != "sprite_waypoint")
949 {
950 backtrace("Trying to disown a non-waypointsprite");
951 return;
952 }
953 if (wp.owner)
954 {
955 if (wp.exteriormodeltoclient == wp.owner)
956 wp.exteriormodeltoclient = NULL;
957 wp.owner.(wp.owned_by_field) = NULL;
958 wp.owner = NULL;
959
961 }
962}
963
965{
966 bool doremove = false;
967
968 if (this.fade_time && time >= this.teleport_time)
969 {
970 doremove = true;
971 }
972
973 if (this.exteriormodeltoclient)
974 WaypointSprite_UpdateOrigin(this, this.exteriormodeltoclient.origin + this.view_ofs);
975
976 if (doremove)
978 else
979 this.nextthink = time; // WHY?!?
980}
981
983{
984 // personal waypoints
985 if (this.enemy && this.enemy != view)
986 return false;
987
988 // team waypoints
989 if (this.rule == SPRITERULE_SPECTATOR)
990 {
992 return false;
993 if (!warmup_stage && IS_PLAYER(view) && autocvar_sv_itemstime != 2)
994 return false;
995 }
996 else if (this.team && this.rule == SPRITERULE_DEFAULT)
997 {
998 if (this.team != view.team)
999 return false;
1000 if (!IS_PLAYER(view))
1001 return false;
1002 }
1003
1004 return true;
1005}
1006
1008{
1009 if (IS_SPEC(e)) e = e.enemy;
1010 /* TODO idea (check this breaks nothing)
1011 else if (e.classname == "observer")
1012 e = NULL;
1013 */
1014 return e;
1015}
1016
1018{
1019 if (teamplay)
1020 return e2.team == e.team;
1021 return e2 == e;
1022}
1023
1025{
1026 // this is not in SendEntity because it shall run every frame, not just every update
1027
1028 // make spectators see what the player would see
1030
1031 if (MUTATOR_CALLHOOK(CustomizeWaypoint, this, client))
1032 return false;
1033
1034 return this.waypointsprite_visible_for_player(this, client, e);
1035}
1036
1037bool WaypointSprite_SendEntity(entity this, entity to, float sendflags);
1038
1040{
1041 // if a WP wants to time out, let it time out immediately; other WPs ought to be reset/killed by their owners
1042
1043 if (this.fade_time)
1044 WaypointSprite_Kill(this);
1045}
1046
1048 entity spr, // sprite
1049 float _lifetime, float maxdistance, // lifetime, max distance
1050 entity ref, vector ofs, // position
1051 entity showto, float t, // show to whom? Use a flag to indicate a team
1052 entity own, .entity ownfield, // remove when own gets killed
1053 float hideable, // true when it should be controlled by cl_hidewaypoints
1054 entity icon // initial icon
1055)
1056{
1057 entity wp = new(sprite_waypoint);
1058 wp.fade_time = _lifetime; // if negative tells client not to fade it out
1059 if(_lifetime < 0)
1060 _lifetime = -_lifetime;
1061 wp.teleport_time = time + _lifetime;
1062 wp.exteriormodeltoclient = ref;
1063 if (ref)
1064 {
1065 wp.view_ofs = ofs;
1066 setorigin(wp, ref.origin + ofs);
1067 }
1068 else
1069 setorigin(wp, ofs);
1070 wp.enemy = showto;
1071 wp.team = t;
1072 wp.owner = own;
1073 wp.currentammo = hideable;
1074 if (own)
1075 {
1076 if (own.(ownfield))
1077 delete(own.(ownfield));
1078 own.(ownfield) = wp;
1079 wp.owned_by_field = ownfield;
1080 }
1081 wp.fade_rate = maxdistance;
1083 wp.nextthink = time;
1084 wp.model1 = spr.netname;
1086 wp.waypointsprite_visible_for_player = WaypointSprite_visible_for_player;
1087 wp.reset2 = WaypointSprite_Reset;
1088 wp.cnt = icon.m_id;
1089 wp.colormod = spr.m_color;
1091 return wp;
1092}
1093
1095 entity spr,
1096 vector ofs,
1097 entity own,
1098 .entity ownfield,
1099 entity icon // initial icon
1100)
1101{
1102 return WaypointSprite_Spawn(spr, 0, 0, NULL, ofs, NULL, 0, own, ownfield, true, icon);
1103}
1104
1106 entity spr,
1107 bool limited_range,
1108 entity player,
1109 vector ofs,
1110 entity icon // initial icon
1111)
1112{
1113 float t;
1114 if (teamplay)
1115 t = player.team;
1116 else
1117 t = 0;
1118 float maxdistance;
1119 if (limited_range)
1120 maxdistance = waypointsprite_limitedrange;
1121 else
1122 maxdistance = 0;
1123 return WaypointSprite_Spawn(spr, waypointsprite_deployed_lifetime, maxdistance, NULL, ofs, NULL, t, player, waypointsprite_deployed_fixed, false, icon);
1124}
1125
1127 entity spr,
1128 entity player,
1129 vector ofs,
1130 entity icon // initial icon
1131)
1132{
1133 return WaypointSprite_Spawn(spr, 0, 0, NULL, ofs, NULL, 0, player, waypointsprite_deployed_personal, false, icon);
1134}
1135
1137 entity spr,
1138 entity player,
1139 bool limited_range,
1140 entity icon // initial icon
1141)
1142{
1143 float t;
1144 if (player.waypointsprite_attachedforcarrier)
1145 return NULL; // can't attach to FC
1146 if (teamplay)
1147 t = player.team;
1148 else
1149 t = 0;
1150 float maxdistance;
1151 if (limited_range)
1152 maxdistance = waypointsprite_limitedrange;
1153 else
1154 maxdistance = 0;
1155 return WaypointSprite_Spawn(spr, waypointsprite_deployed_lifetime, maxdistance, player, '0 0 64', NULL, t, player, waypointsprite_attached, false, icon);
1156}
1157
1159 entity spr,
1160 entity carrier,
1161 entity icon // initial icon and color
1162)
1163{
1164 WaypointSprite_Kill(carrier.waypointsprite_attached); // FC overrides attached
1165 entity e = WaypointSprite_Spawn(spr, 0, 0, carrier, '0 0 64', NULL, carrier.team, carrier, waypointsprite_attachedforcarrier, false, icon);
1166 if (GetResource(carrier, RES_HEALTH))
1167 {
1170 }
1171 return e;
1172}
1173
1175{
1176 WaypointSprite_Disown(carrier.waypointsprite_attachedforcarrier, waypointsprite_deadlifetime);
1177}
1178
1183
1190
1196
1204#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.
bool SetResourceExplicit(entity e, Resource res_type, float amount)
Sets the resource amount of an entity without calling any hooks.
#define drawstring(position, text, scale, rgb, alpha, flag)
Definition draw.qh:27
#define drawpic(position, pic, size, rgb, alpha, flag)
Definition draw.qh:21
#define draw_beginBoldFont()
Definition draw.qh:4
#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:123
ERASEABLE vector hsv_to_rgb(vector hsv)
Definition color.qh:141
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:1389
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:130
int entcs_GetTeam(int i)
Definition ent_cs.qh:133
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:8
noref float vid_conheight
Definition draw.qh:9
#define NET_HANDLE(id, param)
Definition net.qh:15
float servertime
Definition net.qh:330
const int MSG_ENTITY
Definition net.qh:156
#define ReadVector()
Definition net.qh:350
#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:82
#define backtrace(msg)
Definition log.qh:99
#define LOG_INFOF(...)
Definition log.qh:66
ERASEABLE string db_get(int db, string key)
Definition map.qh:91
ERASEABLE void db_put(int db, string key, string value)
Definition map.qh:101
const float M_SQRT2
Definition mathlib.qh:114
#define M_PI
Definition mathlib.qh:108
float stof(string val,...)
float bound(float min, float value, float max)
string substring(string s, float start, float length)
void WriteString(string data, float dest, float desto)
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)
void WriteByte(float data, float dest, float desto)
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:366
f1
Definition all.inc:563
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:43
#define setthink(e, f)
vector
Definition self.qh:92
#define setcefc(e, f)
vector org
Definition self.qh:92
vector vector ang
Definition self.qh:92
float fade_time
Definition common.qh:23
#define STATIC_INIT(func)
during worldspawn
Definition static.qh:32
#define strfree(this)
Definition string.qh:59
#define strcpy(this, s)
Definition string.qh:52
entity enemy
Definition sv_ctf.qh:153
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:45
ERASEABLE vector Rotate(vector v, float a)
Definition vector.qh:105
const vector eX
Definition vector.qh:44
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
Definition weapon.qh:260
float start_armorvalue
Definition world.qh:97
float start_health
Definition world.qh:96