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 = 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 = 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;
582
583 d = o - '0.5 0 0' * vid_conwidth - '0 0.5 0' * vid_conheight;
584 ang = atan2(-d.x, -d.y);
585 if (o.z < 0)
586 ang += M_PI;
587
588 float f1 = d.x / vid_conwidth;
589 float f2 = d.y / vid_conheight;
590 if (f1 == 0) { f1 = 0.000001; }
591 if (f2 == 0) { f2 = 0.000001; }
592
593 if (max(f1, -f1) > max(f2, -f2)) {
594 if (d.z * f1 > 0) {
595 // RIGHT edge
596 d = d * ((0.5 - waypointsprite_edgeoffset_right) / f1);
597 } else {
598 // LEFT edge
599 d = d * (-(0.5 - waypointsprite_edgeoffset_left) / f1);
600 }
601 } else {
602 if (d.z * f2 > 0) {
603 // BOTTOM edge
604 d = d * ((0.5 - waypointsprite_edgeoffset_bottom) / f2);
605 } else {
606 // TOP edge
607 d = d * (-(0.5 - waypointsprite_edgeoffset_top) / f2);
608 }
609 }
610
611 o = d + '0.5 0 0' * vid_conwidth + '0 0.5 0' * vid_conheight;
612 }
613 else
614 {
615#if 1
616 ang = M_PI;
617#else
618 vector d;
619 d = o - '0.5 0 0' * vid_conwidth - '0 0.5 0' * vid_conheight;
620 ang = atan2(-d.x, -d.y);
621#endif
622 }
623 o.z = 0;
624
625 float edgedistance_min = min((o.y - (vid_conheight * waypointsprite_edgeoffset_top)),
629
630 float crosshairdistance = sqrt( ((o.x - vid_conwidth/2) ** 2) + ((o.y - vid_conheight/2) ** 2) );
631
634
635 {
638 }
639 if (edgedistance_min < waypointsprite_edgefadedistance) {
640 a = a * (1 - (1 - waypointsprite_edgefadealpha) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1)));
641 t = t * (1 - (1 - waypointsprite_edgefadescale) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1)));
642 }
643 if (crosshairdistance < waypointsprite_crosshairfadedistance) {
644 a = a * (1 - (1 - waypointsprite_crosshairfadealpha) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1)));
645 t = t * (1 - (1 - waypointsprite_crosshairfadescale) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1)));
646 }
647
648 if (this.build_finished)
649 {
650 if (time < this.build_finished + 0.25)
651 {
652 if (time < this.build_started)
653 SetResourceExplicit(this, RES_HEALTH, this.build_starthealth);
654 else if (time < this.build_finished)
655 SetResourceExplicit(this, RES_HEALTH, (time - this.build_started) / (this.build_finished - this.build_started) * (1 - this.build_starthealth) + this.build_starthealth);
656 else
657 SetResourceExplicit(this, RES_HEALTH, 1);
658 }
659 else
660 SetResourceExplicit(this, RES_HEALTH, -1);
661 }
662
663 o = drawspritearrow(o, ang, rgb, a, SPRITE_ARROW_SCALE * t);
664
665 string pic = "";
666 bool is_text = true;
668 {
669 string spr_icon = spritelookupicon(this, spriteimage);
670 pic = spr_icon;
671 bool icon_found = !(!spr_icon || spr_icon == "");
672 if (icon_found) // it's valid, but let's make sure it exists!
673 {
674 pic = strcat(hud_skin_path, "/", spr_icon);
675 if(precache_pic(pic) == "")
676 {
677 pic = strcat("gfx/hud/default/", spr_icon);
678 if(!precache_pic(pic))
679 icon_found = false;
680 }
681 }
682 if (icon_found)
683 is_text = false;
684 }
685
686 vector sz;
687 vector col = rgb;
688 string txt = string_null; // it will contain either the text or the icon path
689 if (is_text)
690 {
691 txt = spritelookuptext(this, spriteimage);
692 if (this.helpme && time < this.helpme)
693 txt = sprintf(_("%s needing help!"), txt);
695 txt = strtoupper(txt);
696 sz = waypointsprite_fontsize * '1 1 0';
697 }
698 else
699 {
700 txt = pic; // icon path
702 col = '1 1 1';
704 {
705 col = rgb_to_hsv(col);
706 col.y *= autocvar_g_waypointsprite_iconcolor; // scale saturation
707 col = hsv_to_rgb(col);
708 }
710 }
711
713 if (GetResource(this, RES_HEALTH) >= 0)
714 {
715 float align = 0, marg;
716 if (this.build_finished)
717 align = 0.5;
718 else
719 align = 0;
720 if (cos(ang) > 0)
722 else
723 marg = SPRITE_HEALTHBAR_MARGIN * t + 0.5 * sz.y;
724
725 float minwidth = (SPRITE_HEALTHBAR_WIDTH + 2 * SPRITE_HEALTHBAR_BORDER) * t;
726 o = drawsprite_TextOrIcon(is_text, o, ang, minwidth, col, a, sz, txt);
728 o,
729 0,
730 GetResource(this, RES_HEALTH),
731 '0 0 0',
732 '0 0 0',
735 marg,
737 align,
738 rgb,
740 rgb,
743 );
744 }
745 else
746 {
747 drawsprite_TextOrIcon(is_text, o, ang, 0, col, a, sz, txt);
748 }
749
751}
752
754{
755 int dh = search_begin(strcat("models/sprites/*_frame*", ext), false, false);
756 if (dh < 0) return;
757 int ext_len = strlen(ext);
758 int n = search_getsize(dh);
759 for (int i = 0; i < n; ++i)
760 {
761 string s = search_getfilename(dh, i);
762 s = substring(s, 15, strlen(s) - 15 - ext_len); // strip models/sprites/ and extension
763
764 int o = strstrofs(s, "_frame", 0);
765 string sname = strcat("/spriteframes/", substring(s, 0, o));
766 string sframes = substring(s, o + 6, strlen(s) - o - 6);
767 int f = stof(sframes) + 1;
768 db_put(tempdb, sname, ftos(max(f, stof(db_get(tempdb, sname)))));
769 }
770 search_end(dh);
771}
772
780{
803
806}
807#endif
808
809#ifdef SVQC
811{
812 string m1 = _m1.netname;
813 string m2 = _m2.netname;
814 string m3 = _m3.netname;
815 if (m1 != e.model1)
816 {
817 e.model1 = m1;
818 e.SendFlags |= 2;
819 }
820 if (m2 != e.model2)
821 {
822 e.model2 = m2;
823 e.SendFlags |= 4;
824 }
825 if (m3 != e.model3)
826 {
827 e.model3 = m3;
828 e.SendFlags |= 8;
829 }
830}
831
833{
834 f = bound(0, f, e.max_health);
835 float step = e.max_health / 40;
836 if ((floor(f / step) != floor(GetResource(e, RES_HEALTH) / step)) || e.pain_finished)
837 {
838 SetResourceExplicit(e, RES_HEALTH, f);
839 e.pain_finished = 0;
840 e.SendFlags |= 0x80;
841 }
842}
843
845{
846 if (f != e.max_health || e.pain_finished)
847 {
848 e.max_health = f;
849 e.pain_finished = 0;
850 e.SendFlags |= 0x80;
851 }
852}
853
855{
856 if (f != e.pain_finished || e.max_health)
857 {
858 e.max_health = 0;
859 e.pain_finished = f;
860 e.SendFlags |= 0x80;
861 }
862}
863
865{
866 if (o != e.origin)
867 {
868 setorigin(e, o);
869 e.SendFlags |= 64;
870 }
871}
872
873void WaypointSprite_UpdateRule(entity e, float t, float r)
874{
875 // no check, as this is never called without doing an actual change (usually only once)
876 e.rule = r;
877 e.team = t;
878 e.SendFlags |= 1;
879}
880
882{
883 // no check, as this is never called without doing an actual change (usually only once)
884 int i = icon.m_id;
885 int new_cnt = (e.cnt & BIT(7)) | (i & BITS(7));
886 if (new_cnt != e.cnt || col != e.colormod)
887 {
888 e.cnt = new_cnt;
889 e.colormod = col;
890 e.SendFlags |= 32;
891 }
892}
893
895{
896 // anti spam
897 if (time < e.waypointsprite_pingtime) return;
898 e.waypointsprite_pingtime = time + 0.3;
899 // ALWAYS sends (this causes a radar circle), thus no check
900 e.cnt |= BIT(7);
901 e.SendFlags |= 32;
902}
903
905{
907 e.waypointsprite_helpmetime = time + waypointsprite_deployed_lifetime;
908 e.SendFlags |= 32;
909}
910
912{
913 if (!e.fade_time)
914 {
915 e.fade_time = t;
916 e.teleport_time = time + t;
917 }
918 else if (t < (e.teleport_time - time))
919 {
920 // accelerate the waypoint's dying
921 // ensure:
922 // (e.teleport_time - time) / wp.fade_time stays
923 // e.teleport_time = time + fadetime
924 float current_fadetime = e.teleport_time - time;
925 e.teleport_time = time + t;
926 if (e.fade_time < 0)
927 e.fade_time = -e.fade_time;
928 e.fade_time = e.fade_time * t / current_fadetime;
929 }
930
931 e.SendFlags |= 16;
932}
933
940
942{
943 if (!wp) return;
944 if (wp.owner) wp.owner.(wp.owned_by_field) = NULL;
945 delete(wp);
946}
947
949{
950 if (!wp) return;
951 if (wp.classname != "sprite_waypoint")
952 {
953 backtrace("Trying to disown a non-waypointsprite");
954 return;
955 }
956 if (wp.owner)
957 {
958 if (wp.exteriormodeltoclient == wp.owner)
959 wp.exteriormodeltoclient = NULL;
960 wp.owner.(wp.owned_by_field) = NULL;
961 wp.owner = NULL;
962
964 }
965}
966
968{
969 bool doremove = false;
970
971 if (this.fade_time && time >= this.teleport_time)
972 {
973 doremove = true;
974 }
975
976 if (this.exteriormodeltoclient)
977 WaypointSprite_UpdateOrigin(this, this.exteriormodeltoclient.origin + this.view_ofs);
978
979 if (doremove)
981 else
982 this.nextthink = time; // WHY?!?
983}
984
986{
987 // personal waypoints
988 if (this.enemy && this.enemy != view)
989 return false;
990
991 // team waypoints
992 if (this.rule == SPRITERULE_SPECTATOR)
993 {
995 return false;
996 if (!warmup_stage && IS_PLAYER(view) && autocvar_sv_itemstime != 2)
997 return false;
998 }
999 else if (this.team && this.rule == SPRITERULE_DEFAULT)
1000 {
1001 if (this.team != view.team)
1002 return false;
1003 if (!IS_PLAYER(view))
1004 return false;
1005 }
1006
1007 return true;
1008}
1009
1011{
1012 if (IS_SPEC(e)) e = e.enemy;
1013 /* TODO idea (check this breaks nothing)
1014 else if (e.classname == "observer")
1015 e = NULL;
1016 */
1017 return e;
1018}
1019
1021{
1022 if (teamplay)
1023 return e2.team == e.team;
1024 return e2 == e;
1025}
1026
1028{
1029 // this is not in SendEntity because it shall run every frame, not just every update
1030
1031 // make spectators see what the player would see
1033
1034 if (MUTATOR_CALLHOOK(CustomizeWaypoint, this, client))
1035 return false;
1036
1037 return this.waypointsprite_visible_for_player(this, client, e);
1038}
1039
1040bool WaypointSprite_SendEntity(entity this, entity to, float sendflags);
1041
1043{
1044 // if a WP wants to time out, let it time out immediately; other WPs ought to be reset/killed by their owners
1045
1046 if (this.fade_time)
1047 WaypointSprite_Kill(this);
1048}
1049
1051 entity spr, // sprite
1052 float _lifetime, float maxdistance, // lifetime, max distance
1053 entity ref, vector ofs, // position
1054 entity showto, float t, // show to whom? Use a flag to indicate a team
1055 entity own, .entity ownfield, // remove when own gets killed
1056 float hideable, // true when it should be controlled by cl_hidewaypoints
1057 entity icon // initial icon
1058)
1059{
1060 entity wp = new(sprite_waypoint);
1061 wp.fade_time = _lifetime; // if negative tells client not to fade it out
1062 if(_lifetime < 0)
1063 _lifetime = -_lifetime;
1064 wp.teleport_time = time + _lifetime;
1065 wp.exteriormodeltoclient = ref;
1066 if (ref)
1067 {
1068 wp.view_ofs = ofs;
1069 setorigin(wp, ref.origin + ofs);
1070 }
1071 else
1072 setorigin(wp, ofs);
1073 wp.enemy = showto;
1074 wp.team = t;
1075 wp.owner = own;
1076 wp.currentammo = hideable;
1077 if (own)
1078 {
1079 if (own.(ownfield))
1080 delete(own.(ownfield));
1081 own.(ownfield) = wp;
1082 wp.owned_by_field = ownfield;
1083 }
1084 wp.fade_rate = maxdistance;
1086 wp.nextthink = time;
1087 wp.model1 = spr.netname;
1089 wp.waypointsprite_visible_for_player = WaypointSprite_visible_for_player;
1090 wp.reset2 = WaypointSprite_Reset;
1091 wp.cnt = icon.m_id;
1092 wp.colormod = spr.m_color;
1094 return wp;
1095}
1096
1098 entity spr,
1099 vector ofs,
1100 entity own,
1101 .entity ownfield,
1102 entity icon // initial icon
1103)
1104{
1105 return WaypointSprite_Spawn(spr, 0, 0, NULL, ofs, NULL, 0, own, ownfield, true, icon);
1106}
1107
1109 entity spr,
1110 bool limited_range,
1111 entity player,
1112 vector ofs,
1113 entity icon // initial icon
1114)
1115{
1116 float t;
1117 if (teamplay)
1118 t = player.team;
1119 else
1120 t = 0;
1121 float maxdistance;
1122 if (limited_range)
1123 maxdistance = waypointsprite_limitedrange;
1124 else
1125 maxdistance = 0;
1126 return WaypointSprite_Spawn(spr, waypointsprite_deployed_lifetime, maxdistance, NULL, ofs, NULL, t, player, waypointsprite_deployed_fixed, false, icon);
1127}
1128
1130 entity spr,
1131 entity player,
1132 vector ofs,
1133 entity icon // initial icon
1134)
1135{
1136 return WaypointSprite_Spawn(spr, 0, 0, NULL, ofs, NULL, 0, player, waypointsprite_deployed_personal, false, icon);
1137}
1138
1140 entity spr,
1141 entity player,
1142 bool limited_range,
1143 entity icon // initial icon
1144)
1145{
1146 float t;
1147 if (player.waypointsprite_attachedforcarrier)
1148 return NULL; // can't attach to FC
1149 if (teamplay)
1150 t = player.team;
1151 else
1152 t = 0;
1153 float maxdistance;
1154 if (limited_range)
1155 maxdistance = waypointsprite_limitedrange;
1156 else
1157 maxdistance = 0;
1158 return WaypointSprite_Spawn(spr, waypointsprite_deployed_lifetime, maxdistance, player, '0 0 64', NULL, t, player, waypointsprite_attached, false, icon);
1159}
1160
1162 entity spr,
1163 entity carrier,
1164 entity icon // initial icon and color
1165)
1166{
1167 WaypointSprite_Kill(carrier.waypointsprite_attached); // FC overrides attached
1168 entity e = WaypointSprite_Spawn(spr, 0, 0, carrier, '0 0 64', NULL, carrier.team, carrier, waypointsprite_attachedforcarrier, false, icon);
1169 if (GetResource(carrier, RES_HEALTH))
1170 {
1173 }
1174 return e;
1175}
1176
1178{
1179 WaypointSprite_Disown(carrier.waypointsprite_attachedforcarrier, waypointsprite_deadlifetime);
1180}
1181
1186
1193
1199
1207#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:216
#define IS_PLAYER(s)
Definition player.qh:243
vector healtharmor_maxdamage(float h, float a, float armorblock, int deathtype)
Definition util.qc:1289
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:348
const int MSG_ENTITY
Definition net.qh:115
#define ReadVector()
Definition net.qh:367
#define WriteHeader(to, id)
Definition net.qh:221
#define REGISTER_NET_LINKED(id)
Definition net.qh:55
void Net_LinkEntity(entity e, bool docull, float dt, bool(entity this, entity to, int sendflags) sendfunc)
Definition net.qh:123
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:6
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:364
f1
Definition all.inc:561
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:218
float start_armorvalue
Definition world.qh:97
float start_health
Definition world.qh:96